refactor(repetition): materialize repetition rows in db

This commit is contained in:
Paul Chavard 2024-11-26 22:08:58 +01:00
parent 5f10486a51
commit 8e582e0be7
No known key found for this signature in database
4 changed files with 70 additions and 36 deletions

View file

@ -308,7 +308,7 @@ module Instructeurs
respond_to do |format| respond_to do |format|
format.turbo_stream do format.turbo_stream do
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_private_attributes_params, dossier.champs.filter(&:private?)) @to_show, @to_hide, @to_update = champs_to_turbo_update(champs_private_attributes_params, dossier.project_champs_private_all)
end end
end end
end end

View file

@ -286,7 +286,7 @@ module Users
respond_to do |format| respond_to do |format|
format.turbo_stream do format.turbo_stream do
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.champs.filter(&:public?)) @to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.project_champs_public_all)
render :update, layout: false render :update, layout: false
end end
end end

View file

@ -4,7 +4,7 @@ module DossierChampsConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
def project_champ(type_de_champ, row_id) def project_champ(type_de_champ, row_id)
check_valid_row_id?(type_de_champ, row_id) check_valid_row_id_on_read?(type_de_champ, row_id)
champ = champs_by_public_id[type_de_champ.public_id(row_id)] champ = champs_by_public_id[type_de_champ.public_id(row_id)]
if champ.nil? || !champ.is_type?(type_de_champ.type_champ) if champ.nil? || !champ.is_type?(type_de_champ.type_champ)
value = type_de_champ.champ_blank?(champ) ? nil : champ.value value = type_de_champ.champ_blank?(champ) ? nil : champ.value
@ -52,6 +52,28 @@ module DossierChampsConcern
filled_champs_public + filled_champs_private filled_champs_public + filled_champs_private
end end
def project_champs_public_all
revision.types_de_champ_public.flat_map do |type_de_champ|
champ = project_champ(type_de_champ, nil)
if type_de_champ.repetition?
[champ] + project_rows_for(type_de_champ).flatten
else
champ
end
end
end
def project_champs_private_all
revision.types_de_champ_private.flat_map do |type_de_champ|
champ = project_champ(type_de_champ, nil)
if type_de_champ.repetition?
[champ] + project_rows_for(type_de_champ).flatten
else
champ
end
end
end
def project_rows_for(type_de_champ) def project_rows_for(type_de_champ)
return [] if !type_de_champ.repetition? return [] if !type_de_champ.repetition?
@ -83,7 +105,11 @@ module DossierChampsConcern
end end
def champ_value_for_tag(type_de_champ, path = :value) def champ_value_for_tag(type_de_champ, path = :value)
champ = filled_champ(type_de_champ, nil) champ = if type_de_champ.repetition?
project_champ(type_de_champ, nil)
else
filled_champ(type_de_champ, nil)
end
type_de_champ.champ_value_for_tag(champ, path) type_de_champ.champ_value_for_tag(champ, path)
end end
@ -109,30 +135,38 @@ module DossierChampsConcern
def repetition_row_ids(type_de_champ) def repetition_row_ids(type_de_champ)
return [] if !type_de_champ.repetition? return [] if !type_de_champ.repetition?
return [] if !revision.in_revision?(type_de_champ.stable_id) return [] unless stable_id_in_revision?(type_de_champ.stable_id)
@repetition_row_ids ||= {}
@repetition_row_ids[type_de_champ.stable_id] ||= begin
rows = champs_in_revision.filter { _1.row? && _1.stable_id == type_de_champ.stable_id }
row_ids = rows.reject(&:discarded?).map(&:row_id)
stable_ids = revision.children_of(type_de_champ).map(&:stable_id) # Legacy rows are rows that have been created before the introduction of the discarded_at column
champs.filter { _1.stable_id.in?(stable_ids) && _1.row_id.present? } # TODO migrate and clean
.map(&:row_id) children_stable_ids = revision.children_of(type_de_champ).map(&:stable_id)
.uniq discarded_row_ids = rows.filter(&:discarded?).map(&:row_id)
.sort legacy_row_ids = champs_in_revision.filter { _1.stable_id.in?(children_stable_ids) && _1.row_id.present? }.map(&:row_id).uniq
row_ids += (legacy_row_ids - discarded_row_ids)
row_ids.uniq.sort
end
end end
def repetition_add_row(type_de_champ, updated_by:) def repetition_add_row(type_de_champ, updated_by:)
raise "Can't add row to non-repetition type de champ" if !type_de_champ.repetition? raise "Can't add row to non-repetition type de champ" if !type_de_champ.repetition?
row_id = ULID.generate row_id = ULID.generate
types_de_champ = revision.children_of(type_de_champ) champ = champ_for_update(type_de_champ, row_id, updated_by:)
self.champs += types_de_champ.map { _1.build_champ(row_id:, updated_by:) } champ.save!
reload_champs_cache reset_champ_cache(champ)
row_id row_id
end end
def repetition_remove_row(type_de_champ, row_id, updated_by:) def repetition_remove_row(type_de_champ, row_id, updated_by:)
raise "Can't remove row from non-repetition type de champ" if !type_de_champ.repetition? raise "Can't remove row from non-repetition type de champ" if !type_de_champ.repetition?
champs.where(row_id:).destroy_all champ = champ_for_update(type_de_champ, row_id, updated_by:)
reload_champs_cache champ.discard!
reset_champ_cache(champ)
end end
def stable_id_in_revision?(stable_id) def stable_id_in_revision?(stable_id)
@ -173,7 +207,7 @@ module DossierChampsConcern
end end
def champ_with_attributes_for_update(type_de_champ, row_id, updated_by:) def champ_with_attributes_for_update(type_de_champ, row_id, updated_by:)
check_valid_row_id?(type_de_champ, row_id) check_valid_row_id_on_write?(type_de_champ, row_id)
attributes = type_de_champ.params_for_champ attributes = type_de_champ.params_for_champ
# TODO: Once we have the right index in place, we should change this to use `create_or_find_by` instead of `find_or_create_by` # TODO: Once we have the right index in place, we should change this to use `create_or_find_by` instead of `find_or_create_by`
champ = champs champ = champs
@ -198,12 +232,22 @@ module DossierChampsConcern
[champ, attributes] [champ, attributes]
end end
def check_valid_row_id?(type_de_champ, row_id) def check_valid_row_id_on_write?(type_de_champ, row_id)
if type_de_champ.repetition?
if row_id.blank?
raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} must have a row_id because it represents a row in a repetition"
end
else
check_valid_row_id_on_read?(type_de_champ, row_id)
end
end
def check_valid_row_id_on_read?(type_de_champ, row_id)
if type_de_champ.child?(revision) if type_de_champ.child?(revision)
if row_id.blank? if row_id.blank?
raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} must have a row_id because it is part of a repetition" raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} must have a row_id because it is part of a repetition"
end end
elsif row_id.present? && type_de_champ.in_revision?(revision) elsif row_id.present? && stable_id_in_revision?(type_de_champ.stable_id)
raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} can not have a row_id because it is not part of a repetition" raise "type_de_champ #{type_de_champ.stable_id} in revision #{revision_id} can not have a row_id because it is not part of a repetition"
end end
end end
@ -214,15 +258,12 @@ module DossierChampsConcern
@filled_champs_private = nil @filled_champs_private = nil
@project_champs_public = nil @project_champs_public = nil
@project_champs_private = nil @project_champs_private = nil
@repetition_row_ids = nil
@revision_stable_ids = nil
end end
def reset_champ_cache(champ) def reset_champ_cache(champ)
champs_by_public_id[champ.public_id]&.reload champs_by_public_id[champ.public_id]&.reload
reset_champs_cache reset_champs_cache
end end
def reload_champs_cache
champs.reload if persisted?
reset_champs_cache
end
end end

View file

@ -1,9 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe DossierChampsConcern do RSpec.describe DossierChampsConcern do
let(:procedure) do let(:procedure) { create(:procedure, types_de_champ_public:, types_de_champ_private:) }
create(:procedure, types_de_champ_public:, types_de_champ_private:)
end
let(:types_de_champ_public) do let(:types_de_champ_public) do
[ [
{ type: :text, libelle: "Un champ text", stable_id: 99 }, { type: :text, libelle: "Un champ text", stable_id: 99 },
@ -47,7 +45,7 @@ RSpec.describe DossierChampsConcern do
let(:row_id) { dossier.project_champ(type_de_champ_repetition, nil).row_ids.first } let(:row_id) { dossier.project_champ(type_de_champ_repetition, nil).row_ids.first }
it { it {
expect(subject.persisted?).to be_truthy expect(subject.new_record?).to be_truthy
expect(subject.row_id).to eq(row_id) expect(subject.row_id).to eq(row_id)
} }
@ -130,10 +128,11 @@ RSpec.describe DossierChampsConcern do
{ type: :explication } { type: :explication }
] ]
end end
let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
subject { dossier.filled_champs_public } subject { dossier.filled_champs_public }
it { expect(subject.size).to eq(4) } it { expect(subject.size).to eq(5) }
it { expect(subject.find { _1.libelle == 'Nom' }).to be_truthy } it { expect(subject.filter { _1.libelle == 'Nom' }.size).to eq(2) }
end end
describe '#filled_champs_private' do describe '#filled_champs_private' do
@ -156,14 +155,8 @@ RSpec.describe DossierChampsConcern do
it { expect(subject.size).to eq(1) } it { expect(subject.size).to eq(1) }
context 'given a type de champ repetition in another revision' do context 'given a type de champ repetition in another revision' do
let(:procedure) { create(:procedure, :published, types_de_champ_public:, types_de_champ_private:) }
let(:draft) { procedure.draft_revision }
let(:errored_stable_id) { 666 }
let(:type_de_champ_repetition) { procedure.active_revision.types_de_champ.find { _1.stable_id == errored_stable_id } }
before do before do
dossier procedure.draft_revision.remove_type_de_champ(type_de_champ_repetition.stable_id)
tdc_repetition = draft.add_type_de_champ(type_champ: :repetition, libelle: "repetition", stable_id: errored_stable_id)
draft.add_type_de_champ(type_champ: :text, libelle: "t1", parent_stable_id: tdc_repetition.stable_id)
procedure.publish_revision! procedure.publish_revision!
end end