diff options
| author | Robert | 2017-10-17 15:50:25 +0200 |
|---|---|---|
| committer | Robert | 2017-10-17 15:50:25 +0200 |
| commit | 81d88f25a13246fe7983b5e0ffbbc50aa10a32d3 (patch) | |
| tree | e65197a1072e907d21cf64f35c85adaeefb17991 | |
| parent | c1ed07eadc07e878ad09feecb529c26623189878 (diff) | |
| parent | 5559c8639f977b2ee946d3f982da1bbf0643a5a4 (diff) | |
| download | chouette-core-81d88f25a13246fe7983b5e0ffbbc50aa10a32d3.tar.bz2 | |
Merge branch 'master' of github.com:af83/stif-boiv
23 files changed, 289 insertions, 16 deletions
diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index 2d24d5aa6..af74f635f 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -130,7 +130,6 @@ class TimeTablesController < ChouetteController scope = select_time_tables if params[:q] && params[:q]["tag_search"] tags = params[:q]["tag_search"].reject {|c| c.empty?} - params[:q].delete("tag_search") scope = select_time_tables.tagged_with(tags, :any => true) if tags.any? end scope = self.ransack_period_range(scope: scope, error_message: t('referentials.errors.validity_period'), query: :overlapping) diff --git a/app/views/compliance_control_blocks/edit.html.slim b/app/views/compliance_control_blocks/edit.html.slim index 0ac507ece..637bb7311 100644 --- a/app/views/compliance_control_blocks/edit.html.slim +++ b/app/views/compliance_control_blocks/edit.html.slim @@ -1,5 +1,5 @@ / PageHeader -= pageheader 'modele-calendrier', += pageheader 'jeux-de-controle', t('compliance_control_blocks.edit.title', compliance_control_block: @compliance_control_block.id) diff --git a/app/views/compliance_control_blocks/new.html.slim b/app/views/compliance_control_blocks/new.html.slim index 654a0dc7f..49404c552 100644 --- a/app/views/compliance_control_blocks/new.html.slim +++ b/app/views/compliance_control_blocks/new.html.slim @@ -1,5 +1,5 @@ / PageHeader -= pageheader 'modele-calendrier', += pageheader 'jeux-de-controle', t('compliance_control_blocks.new.title') diff --git a/app/views/compliance_control_sets/edit.html.slim b/app/views/compliance_control_sets/edit.html.slim index 934bd81b0..649154b91 100644 --- a/app/views/compliance_control_sets/edit.html.slim +++ b/app/views/compliance_control_sets/edit.html.slim @@ -1,5 +1,5 @@ / PageHeader -= pageheader 'modele-calendrier', += pageheader 'jeux-de-controle', t('compliance_control_sets.edit.title', name: @compliance_control_set.name) / PageContent diff --git a/app/views/compliance_control_sets/index.html.slim b/app/views/compliance_control_sets/index.html.slim index 68173fee9..1120ed186 100644 --- a/app/views/compliance_control_sets/index.html.slim +++ b/app/views/compliance_control_sets/index.html.slim @@ -1,5 +1,5 @@ / PageHeader -- header_params = ['jeux-de-donnees', +- header_params = ['jeux-de-controle', t('compliance_control_sets.index.title'), ''] - header_params << link_to(t('compliance_control_sets.actions.new'), new_compliance_control_set_path, class: 'btn btn-default') if policy(Calendar).create? diff --git a/app/views/compliance_control_sets/new.html.slim b/app/views/compliance_control_sets/new.html.slim index d6be41ee8..35654b4d6 100644 --- a/app/views/compliance_control_sets/new.html.slim +++ b/app/views/compliance_control_sets/new.html.slim @@ -1,5 +1,5 @@ / PageHeader -= pageheader 'modele-calendrier', += pageheader 'jeux-de-controle', t('compliance_control_sets.index.new') diff --git a/app/views/compliance_control_sets/show.html.slim b/app/views/compliance_control_sets/show.html.slim index b6e203a9e..7767bd0d9 100644 --- a/app/views/compliance_control_sets/show.html.slim +++ b/app/views/compliance_control_sets/show.html.slim @@ -1,5 +1,5 @@ / PageHeader -= pageheader 'jeux-de-donnees', += pageheader 'jeux-de-controle', t('compliance_control_sets.show.title', name: @compliance_control_set.name), 'Lorem ipsum dolor sit amet' diff --git a/app/views/compliance_controls/edit.html.slim b/app/views/compliance_controls/edit.html.slim index d7497c0e2..1d478e845 100644 --- a/app/views/compliance_controls/edit.html.slim +++ b/app/views/compliance_controls/edit.html.slim @@ -1,4 +1,4 @@ -= pageheader 'compliance-control', += pageheader 'jeux-de-controle', t('compliance_controls.edit.title') diff --git a/app/views/compliance_controls/new.html.slim b/app/views/compliance_controls/new.html.slim index 962f70ecc..181f49a15 100644 --- a/app/views/compliance_controls/new.html.slim +++ b/app/views/compliance_controls/new.html.slim @@ -1,5 +1,5 @@ / PageHeader -- header_params = ['jeux-de-donnees', +- header_params = ['jeux-de-controle', t('compliance_controls.new.title'), ''] = pageheader(*header_params) do diff --git a/app/views/compliance_controls/select_type.html.slim b/app/views/compliance_controls/select_type.html.slim index c6e3b0427..98cc5a943 100644 --- a/app/views/compliance_controls/select_type.html.slim +++ b/app/views/compliance_controls/select_type.html.slim @@ -1,5 +1,5 @@ / PageHeader -- header_params = ['jeux-de-donnees', +- header_params = ['jeux-de-controle', t('compliance_controls.select_type.title'), ''] = pageheader(*header_params) do diff --git a/app/views/compliance_controls/show.html.slim b/app/views/compliance_controls/show.html.slim index 8232dbe28..7f11b1041 100644 --- a/app/views/compliance_controls/show.html.slim +++ b/app/views/compliance_controls/show.html.slim @@ -1,7 +1,9 @@ / PageHeader -= pageheader 'jeux-de-donnees', +- header_params = ['jeux-de-controle', t('compliance_controls.show.title'), - '' + ''] += pageheader(*header_params) do + / PageContent .page_content .container-fluid diff --git a/app/views/time_tables/_filter.html.slim b/app/views/time_tables/_filter.html.slim index 2672f7dfc..11e9987c4 100644 --- a/app/views/time_tables/_filter.html.slim +++ b/app/views/time_tables/_filter.html.slim @@ -9,7 +9,7 @@ .ffg-row .form-group = f.label Chouette::TimeTable.human_attribute_name(:tag_search), required: false, class: 'control-label' - = f.input :tag_search, as: :tags, collection: Chouette::TimeTable.tags_on(:tags).pluck(:name), label: false, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une étiquette...' }, wrapper_html: { class: 'select2ed'}, include_blank: false + = f.input :tag_search, as: :tags, collection: Chouette::TimeTable.tags_on(:tags).pluck(:name), label: false, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez une étiquette...' }, wrapper_html: { class: 'select2ed'}, include_blank: false, selected: params[:q] ? params[:q]['tag_search'] : nil .form-group.togglable = f.label Chouette::TimeTable.human_attribute_name(:bounding_dates), required: false, class: 'control-label' diff --git a/app/workers/compliance_control_set_cloning_worker.rb b/app/workers/compliance_control_set_cloning_worker.rb new file mode 100644 index 000000000..9cbe5c81a --- /dev/null +++ b/app/workers/compliance_control_set_cloning_worker.rb @@ -0,0 +1,8 @@ +class ComplianceControlSetCloningWorker + include Sidekiq::Worker + + def perform id, organisation_id + ComplianceControlSetCloner.new.copy id, organisation_id + end + +end diff --git a/config/locales/compliance_control_blocks.en.yml b/config/locales/compliance_control_blocks.en.yml index a37b41db5..fbface6b2 100644 --- a/config/locales/compliance_control_blocks.en.yml +++ b/config/locales/compliance_control_blocks.en.yml @@ -10,9 +10,11 @@ fr: transport_mode: Transport mode sub_transport_mode: Transport submode compliance_control_blocks: + clone: + prefix: 'Copy of' actions: destroy_confirm: Are you sure you want to destroy this block ? new: title: Create a control block edit: - title: "Edit the control block : %{compliance_control_block}"
\ No newline at end of file + title: "Edit the control block : %{compliance_control_block}" diff --git a/config/locales/compliance_control_blocks.fr.yml b/config/locales/compliance_control_blocks.fr.yml index f93cafa54..66df008be 100644 --- a/config/locales/compliance_control_blocks.fr.yml +++ b/config/locales/compliance_control_blocks.fr.yml @@ -10,9 +10,11 @@ fr: transport_mode: Mode de transport transport_submode: Sous-mode de transport compliance_control_blocks: + clone: + prefix: 'Copie de' actions: destroy_confirm: Etes vous sûr de supprimer ce bloc ? new: title: Créer un groupe de contrôle(s) edit: - title: "Editer le groupe de contrôle : %{compliance_control_block}"
\ No newline at end of file + title: "Editer le groupe de contrôle : %{compliance_control_block}" diff --git a/config/locales/compliance_control_sets.en.yml b/config/locales/compliance_control_sets.en.yml index 83b14642c..f72342894 100644 --- a/config/locales/compliance_control_sets.en.yml +++ b/config/locales/compliance_control_sets.en.yml @@ -1,5 +1,7 @@ en: compliance_control_sets: + clone: + prefix: 'Copie de' index: title: Compliance control set new: New compliance control set diff --git a/config/locales/compliance_control_sets.fr.yml b/config/locales/compliance_control_sets.fr.yml index 37851d7c4..c31eb9423 100644 --- a/config/locales/compliance_control_sets.fr.yml +++ b/config/locales/compliance_control_sets.fr.yml @@ -1,5 +1,7 @@ fr: compliance_control_sets: + clone: + prefix: 'Copy of' index: title: "Liste des jeux de contrôles" edit: diff --git a/config/locales/compliance_controls.en.yml b/config/locales/compliance_controls.en.yml index 3063c35a4..887bc2009 100644 --- a/config/locales/compliance_controls.en.yml +++ b/config/locales/compliance_controls.en.yml @@ -1,5 +1,7 @@ en: compliance_controls: + clone: + prefix: 'Copy of' min_max_values: "the minimum (%{min}) is not supposed to be greater than the maximum (%{max})" errors: incoherent_control_sets: "Impossible to assign a control to a set (id: %{direct_set_name}) differing from the one of its group (id: %{indirect_set_name})" diff --git a/config/locales/compliance_controls.fr.yml b/config/locales/compliance_controls.fr.yml index 2038b9eb7..2feb201bf 100644 --- a/config/locales/compliance_controls.fr.yml +++ b/config/locales/compliance_controls.fr.yml @@ -1,5 +1,7 @@ fr: compliance_controls: + clone: + prefix: 'Copie de' min_max_values: "la valeur de minimum (%{min}) ne doit pas être superieur à la valuer du maximum (%{max})" errors: incoherent_control_sets: "Le contrôle ne peut pas être associé à un jeu de contrôle (id: %{direct_set_name}) différent de celui de son groupe (id: %{indirect_set_name})" diff --git a/lib/compliance_control_set_cloner.rb b/lib/compliance_control_set_cloner.rb new file mode 100644 index 000000000..1cf58a38d --- /dev/null +++ b/lib/compliance_control_set_cloner.rb @@ -0,0 +1,94 @@ +class ComplianceControlSetCloner + + # Naming Convention: As we are in a domain with quite long names we + # abbreviate compliance_control to cc and + # compliance_check to cck iff used as prefixes. + + attr_reader :organisation_id, :source_set_id + + def copy source_set_id, organisation_id + @source_set_id = source_set_id + @organisation_id = organisation_id + copy_set + end + + + private + + # Workers + # ------- + + # Copy Set: + def copy_set + # Force lazy creation of target_set, just in case source_set is _empty_. + target_set + copy_controls + copy_blocks + end + + # Copy Blocks: + def copy_block source_block + target_set.compliance_control_blocks.create( + name: name_of_copy(:compliance_control_blocks, source_block.name), + condition_attributes: source_block.condition_attributes).tap do | target_block | + relink_checks_to_block source_block, target_block + end + end + def copy_blocks + source_set.compliance_control_blocks.order(:id).each(&method(:copy_block)) + end + def relink_checks_to_block source_block, target_block + source_block + .compliance_controls + .order(:id) + .each do | source_control | + control_id_map[source_control.id] + .update(compliance_control_block_id: target_block.id) + end + end + + # Copy Controls: + def copy_controls + source_set.compliance_controls.order(:id).each(&method(:copy_control)) + end + def copy_control(compliance_control) + target_set.compliance_controls.create( + code: compliance_control.code, + comment: compliance_control.comment, + control_attributes: compliance_control.control_attributes, + criticity: compliance_control.criticity, + name: name_of_copy(:compliance_controls, compliance_control.name), + origin_code: compliance_control.origin_code, + target: compliance_control.target, + type: compliance_control.type + ).tap do | control | + control_id_map.update compliance_control.id => control + end + end + + def name_of_copy resource, name + [I18n.t("#{resource}.clone.prefix"), name].join(' ') + end + + # Lazy Values + # ----------- + def organisation + @__organisation__ ||= Organisation.find(organisation_id) + end + def source_set + @__source_set__ ||= ComplianceControlSet.find(source_set_id) + end + def target_set + @__target_set__ ||= ComplianceControlSet.create!( + organisation: organisation, + name: name_of_copy(:compliance_control_sets, source_set.name) + ) + end + def control_id_map + # Map: compliance_control_id -> compliance_control (origin_id -> copied object) + @__control_id_to_check__ ||= Hash.new + end + def referential + @__referential__ ||= Referential.find(referential_id) + end +end diff --git a/spec/lib/compliance_control_set_cloner_spec.rb b/spec/lib/compliance_control_set_cloner_spec.rb new file mode 100644 index 000000000..4305ec70b --- /dev/null +++ b/spec/lib/compliance_control_set_cloner_spec.rb @@ -0,0 +1,144 @@ +RSpec.describe ComplianceControlSetCloner do + + subject{ described_class.new } + + let( :new_organisation ){ create :organisation } + + let( :source_set ){ create :compliance_control_set } + let( :set_prefix ){ I18n.t('compliance_control_sets.clone.prefix') } + let( :block_prefix ){ I18n.t('compliance_control_blocks.clone.prefix') } + let( :control_prefix ){ I18n.t('compliance_controls.clone.prefix') } + + + context 'Copying empty set' do + + context 'correct organisation' do + + # + # + # + # + # + # + # + # +-------------------+ + # +---------------------+----------------| Control (direct0) | + # | | +-------------------+ + # | | + # | | +-------------------+ + # +---------------------)------------+---| Control (direct1) | + # | | | +-------------------+ + # | | | + # | | | +-------------------+ + # +---------------------)------------)---| Control (direct2) | + # | | | +-------------------+ + # | | | + # | | | + # | | | + # v v | + # +------------+ +--------------+ | +---------------------+ + # | ControlSet |<----+----| ControlBlock |<-)--| Control (indirect0) | + # +------------+ | +--------------+ | +---------------------+ + # | | + # | +--------------+<-+ +---------------------+ + # |<---| ControlBlock |<----| Control (indirect1) | + # | +--------------+ +---------------------+ + # | + # | +--------------+ +---------------------+ + # +----| ControlBlock |<----| Control (indirect2) | + # +--------------+ +---------------------+ + + context 'Directed Acyclic Graph is copied correctly' do + let(:source_blox){ + 3.times.map{ |_| create :compliance_control_block, compliance_control_set: source_set } + } + let(:direct_ccs){ + 3.times.map{ |n| create :generic_attribute_control_min_max, compliance_control_set: source_set, name: "direct #{n.succ}", code: "direct-#{n.succ}" } + } + # Needed to check we do not dulicate a node (compliance_control) twice + let(:indirect_ccs){ + # Create 1 child for each block and also associate first of the direct ccs to the first block + # seconf of the direct css to the second block + source_blox.take(2).zip(direct_ccs.take(2)).each do | source_block, cc | + cc.update compliance_control_block_id: source_block.id + end + source_blox.each_with_index.map{ | source_block, n | + create(:generic_attribute_control_min_max, compliance_control_set: source_set, compliance_control_block: source_block, name: "indirect #{n.succ}", code: "indirect-#{n.succ}") + } + } + let( :sources ){ source_set.compliance_controls.order(:id) } + + let( :target_set ){ ComplianceControlSet.last } + let( :target_blox ){ ComplianceControlBlock.last 3 } + let( :targets ){ target_set.compliance_controls.order(:id) } + + before do + direct_ccs + indirect_ccs + end + it 'correctly creates a set for a complete DAG' do + # Slowness of tests constrains us to create a minimum of objects in the DB, + # hence only one example :( + # + # Execute copy and keep count + counts = object_counts + subject.copy(source_set.id, new_organisation.id) + delta = count_diff counts, object_counts + + # Check correctly copied set + expect(target_set.organisation).to eq(new_organisation) + expect(target_set.name).to eq( [set_prefix, source_set.name].join(' ') ) + + # Check correctly copied controls + targets.zip(sources).each do | target, source | + expect( target.code ).to eq(source.code ) + expect( target.comment ).to eq(source.comment ) + expect( target.compliance_control_set ).to eq( target_set ) + expect( target.control_attributes ).to eq(source.control_attributes) + expect( target.criticity ).to eq(source.criticity ) + expect( target.name ).to eq([control_prefix, source.name].join(' ')) + expect( target.origin_code ).to eq(source.origin_code ) + expect( target.type ).to eq(source.type) + end + # Check correctly copied blocks + target_blox.zip(source_blox).each do | target_block, source_block | + expect( target_block.compliance_control_set ).to eq(target_set) + expect( target_block.name ).to eq( [block_prefix, source_block.name].join(' ') ) + expect( target_block.condition_attributes ).to eq( source_block.condition_attributes ) + end + + # Check correct block associations + # See diagram above to understand the meaning of this: + # - The first two controls have been assigned to the first two blocks accordingly + # - The third has no block + # - The last three controls have been created from the three blocks in order + expected_block_ids = target_blox.take(2).map(&:id) + [ nil ] + target_blox.map(&:id) + expect( targets.pluck(:compliance_control_block_id) ).to eq( expected_block_ids ) + + # Check overall counts (no additional creations) + expect( delta ).to eq(counts) + end + end + + end + + def object_counts + { + source_set_count: ComplianceControlSet.count, + cc_block_count: ComplianceControlBlock.count, + cc_count: ComplianceControl.count, + cck_set_count: ComplianceCheckSet.count, + cck_block_count: ComplianceCheckBlock.count, + cck_count: ComplianceCheck.count + } + end + + def count_diff count1, count2 + count1.inject({}){ |h, (k,v)| + h.merge( k => count2[k] - v ) + } + end + + end + +end diff --git a/spec/workers/clean_up_worker_spec.rb b/spec/workers/clean_up_worker_spec.rb index e85768fa3..fd767db00 100644 --- a/spec/workers/clean_up_worker_spec.rb +++ b/spec/workers/clean_up_worker_spec.rb @@ -1,4 +1,3 @@ -require 'rails_helper' RSpec.describe CleanUpWorker, type: :worker do pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/workers/compliance_control_set_cloning_worker_spec.rb b/spec/workers/compliance_control_set_cloning_worker_spec.rb new file mode 100644 index 000000000..3a2332f62 --- /dev/null +++ b/spec/workers/compliance_control_set_cloning_worker_spec.rb @@ -0,0 +1,15 @@ +RSpec.describe ComplianceControlSetCloningWorker do + + + it 'is a worker' do + expect( described_class.new ).to be_a(Sidekiq::Worker) + end + + it 'delegates perform to the correct lib call' do + id = double('id') + organisation_id = double('organisation_id') + expect_any_instance_of(ComplianceControlSetCloner).to receive(:copy).with(id, organisation_id) + described_class.new.perform(id, organisation_id) + end + +end |
