refactor(repetition): add row_id to champs

This commit is contained in:
Paul Chavard 2022-12-16 12:39:51 +01:00
parent 5a7b2f76ee
commit dfe65136be
44 changed files with 106 additions and 7 deletions

View file

@ -84,6 +84,7 @@ gem 'spreadsheet_architect'
gem 'strong_migrations' # lint database migrations gem 'strong_migrations' # lint database migrations
gem 'turbo-rails' gem 'turbo-rails'
gem 'typhoeus' gem 'typhoeus'
gem 'ulid-ruby', require: 'ulid'
gem 'view_component' gem 'view_component'
gem 'vite_rails' gem 'vite_rails'
gem 'warden' gem 'warden'

View file

@ -726,6 +726,7 @@ GEM
ethon (>= 0.9.0) ethon (>= 0.9.0)
tzinfo (2.0.5) tzinfo (2.0.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
ulid-ruby (1.0.2)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.7) unf_ext (0.0.7.7)
@ -915,6 +916,7 @@ DEPENDENCIES
timecop timecop
turbo-rails turbo-rails
typhoeus typhoeus
ulid-ruby
vcr vcr
view_component view_component
vite_rails vite_rails

View file

@ -0,0 +1,7 @@
class Migrations::BackfillRowIdJob < ApplicationJob
def perform(batch)
batch.each do |(row_id, champ_ids)|
Champ.where(id: champ_ids).update_all(row_id:)
end
end
end

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champ < ApplicationRecord class Champ < ApplicationRecord
@ -221,7 +222,7 @@ class Champ < ApplicationRecord
end end
def clone def clone
champ_attributes = [:parent_id, :private, :row, :type, :type_de_champ_id] champ_attributes = [:parent_id, :private, :row, :row_id, :type, :type_de_champ_id]
value_attributes = private? ? [] : [:value, :value_json, :data, :external_id] value_attributes = private? ? [] : [:value, :value_json, :data, :external_id]
relationships = private? ? [] : [:etablissement, :geo_areas] relationships = private? ? [] : [:etablissement, :geo_areas]

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::AddressChamp < Champs::TextChamp class Champs::AddressChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::AnnuaireEducationChamp < Champs::TextChamp class Champs::AnnuaireEducationChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::CarteChamp < Champ class Champs::CarteChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::CheckboxChamp < Champs::BooleanChamp class Champs::CheckboxChamp < Champs::BooleanChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::CiviliteChamp < Champ class Champs::CiviliteChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::CnafChamp < Champs::TextChamp class Champs::CnafChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::CommuneChamp < Champs::TextChamp class Champs::CommuneChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DateChamp < Champ class Champs::DateChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DatetimeChamp < Champ class Champs::DatetimeChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DecimalNumberChamp < Champ class Champs::DecimalNumberChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DepartementChamp < Champs::TextChamp class Champs::DepartementChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DgfipChamp < Champs::TextChamp class Champs::DgfipChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DossierLinkChamp < Champ class Champs::DossierLinkChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::DropDownListChamp < Champ class Champs::DropDownListChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::EmailChamp < Champs::TextChamp class Champs::EmailChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::ExplicationChamp < Champs::TextChamp class Champs::ExplicationChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::HeaderSectionChamp < Champ class Champs::HeaderSectionChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::IbanChamp < Champ class Champs::IbanChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::IntegerNumberChamp < Champ class Champs::IntegerNumberChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::LinkedDropDownListChamp < Champ class Champs::LinkedDropDownListChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::MesriChamp < Champs::TextChamp class Champs::MesriChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::MultipleDropDownListChamp < Champ class Champs::MultipleDropDownListChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::NumberChamp < Champ class Champs::NumberChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::PaysChamp < Champs::TextChamp class Champs::PaysChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::PhoneChamp < Champs::TextChamp class Champs::PhoneChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::PieceJustificativeChamp < Champ class Champs::PieceJustificativeChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::PoleEmploiChamp < Champs::TextChamp class Champs::PoleEmploiChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::RegionChamp < Champs::TextChamp class Champs::RegionChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::RepetitionChamp < Champ class Champs::RepetitionChamp < Champ
@ -32,8 +33,9 @@ class Champs::RepetitionChamp < Champ
added_champs = [] added_champs = []
transaction do transaction do
row = (blank? ? -1 : champs.last.row) + 1 row = (blank? ? -1 : champs.last.row) + 1
row_id = ULID.generate
revision.children_of(type_de_champ).each do |type_de_champ| revision.children_of(type_de_champ).each do |type_de_champ|
added_champs << type_de_champ.champ.build(row: row) added_champs << type_de_champ.champ.build(row:, row_id:)
end end
self.champs << added_champs self.champs << added_champs
end end

View file

@ -19,6 +19,7 @@
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# type_de_champ_id :integer not null # type_de_champ_id :integer not null
# row_id :string
# #
class Champs::RNAChamp < Champ class Champs::RNAChamp < Champ
validates :value, allow_blank: true, format: { validates :value, allow_blank: true, format: {

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::SiretChamp < Champ class Champs::SiretChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::TextChamp < Champ class Champs::TextChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::TextareaChamp < Champs::TextChamp class Champs::TextareaChamp < Champs::TextChamp

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::TitreIdentiteChamp < Champ class Champs::TitreIdentiteChamp < Champ

View file

@ -18,6 +18,7 @@
# etablissement_id :integer # etablissement_id :integer
# external_id :string # external_id :string
# parent_id :bigint # parent_id :bigint
# row_id :string
# type_de_champ_id :integer # type_de_champ_id :integer
# #
class Champs::YesNoChamp < Champs::BooleanChamp class Champs::YesNoChamp < Champs::BooleanChamp

View file

@ -112,8 +112,8 @@ module DossierRebaseConcern
.where(type_de_champ: { stable_id: parent_stable_id }) .where(type_de_champ: { stable_id: parent_stable_id })
champs_repetition.each do |champ_repetition| champs_repetition.each do |champ_repetition|
champ_repetition.champs.map(&:row).uniq.each do |row| champ_repetition.champs.index_by(&:row).each do |(row, champ)|
create_champ(target_coordinate, champ_repetition, row: row) create_champ(target_coordinate, champ_repetition, row:, row_id: champ.row_id)
end end
end end
else else
@ -121,9 +121,8 @@ module DossierRebaseConcern
end end
end end
def create_champ(target_coordinate, parent, row: nil) def create_champ(target_coordinate, parent, row: nil, row_id: nil)
params = { revision: target_coordinate.revision } params = { revision: target_coordinate.revision, row:, row_id: }.compact
params[:row] = row if row
champ = target_coordinate champ = target_coordinate
.type_de_champ .type_de_champ
.build_champ(params) .build_champ(params)

View file

@ -0,0 +1,5 @@
class AddRowIdToChamps < ActiveRecord::Migration[6.1]
def change
add_column :champs, :row_id, :string
end
end

View file

@ -0,0 +1,7 @@
class AddRowIdIndexToChamps < ActiveRecord::Migration[6.1]
disable_ddl_transaction!
def change
add_index :champs, :row_id, algorithm: :concurrently
end
end

View file

@ -227,6 +227,7 @@ ActiveRecord::Schema.define(version: 2022_12_27_084442) do
t.boolean "private", default: false, null: false t.boolean "private", default: false, null: false
t.datetime "rebased_at" t.datetime "rebased_at"
t.integer "row" t.integer "row"
t.string "row_id"
t.string "type" t.string "type"
t.integer "type_de_champ_id", null: false t.integer "type_de_champ_id", null: false
t.datetime "updated_at" t.datetime "updated_at"
@ -237,6 +238,7 @@ ActiveRecord::Schema.define(version: 2022_12_27_084442) do
t.index ["parent_id"], name: "index_champs_on_parent_id" t.index ["parent_id"], name: "index_champs_on_parent_id"
t.index ["private"], name: "index_champs_on_private" t.index ["private"], name: "index_champs_on_private"
t.index ["row"], name: "index_champs_on_row" t.index ["row"], name: "index_champs_on_row"
t.index ["row_id"], name: "index_champs_on_row_id"
t.index ["type"], name: "index_champs_on_type" t.index ["type"], name: "index_champs_on_type"
t.index ["type_de_champ_id", "dossier_id", "row"], name: "index_champs_on_type_de_champ_id_and_dossier_id_and_row", unique: true t.index ["type_de_champ_id", "dossier_id", "row"], name: "index_champs_on_type_de_champ_id_and_dossier_id_and_row", unique: true
t.index ["type_de_champ_id"], name: "index_champs_on_type_de_champ_id" t.index ["type_de_champ_id"], name: "index_champs_on_type_de_champ_id"

View file

@ -0,0 +1,39 @@
namespace :after_party do
desc 'Deployment task: backfill_row_id_on_champs'
task backfill_row_id_on_champs: :environment do
puts "Running deploy task 'backfill_row_id_on_champs'"
champs_to_fill = Champ.where(row_id: nil).where.not(parent_id: nil)
progress = ProgressReport.new(champs_to_fill.count)
champs = champs_to_fill.pluck(:parent_id, :row, :id)
pp "found #{champs.size} champs to fill"
row_ids = Champ.where.not(row_id: nil)
.distinct(:row_id)
.pluck(:parent_id, :row, :row_id)
.map { [[_1.first, _1.second], _1.last] }.to_h
pp "found #{row_ids.size} existing row ids"
champs_by_row = champs.group_by { [_1.first, _1.second] }.transform_values { _1.map(&:last) }
pp "found #{champs_by_row.size} rows to fill"
champs_by_row.to_a.sort_by(&:first).in_groups_of(5_000) do |batch|
batch = batch.lazy.compact.flat_map do |(key, champs)|
row_ids[key] ||= ULID.generate
champs&.map { [row_ids[key], _1] } || []
end.group_by(&:first).transform_values { _1.map(&:last) }
Migrations::BackfillRowIdJob.perform_later(batch)
progress.inc(batch.size)
end
progress.finish
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end