diff options
| author | Luc Donnet | 2016-11-03 11:02:28 +0100 |
|---|---|---|
| committer | Luc Donnet | 2016-11-03 11:02:28 +0100 |
| commit | 552611c5a96134eff8515bbfe32870e900225bfb (patch) | |
| tree | c3b7cc795d60d390457d201feb7cb845f4b433dd | |
| parent | 7212356ab15ea1807c28a8dce4a11290d47facac (diff) | |
| parent | d67ed4c32b338070e4e4ff33f89fe64011e14c3b (diff) | |
| download | chouette-core-552611c5a96134eff8515bbfe32870e900225bfb.tar.bz2 | |
Merge branch 'master' of github.com:AF83/stif-boiv
18 files changed, 208 insertions, 23 deletions
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 4ffb6ad65..3ae59f975 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -7,6 +7,7 @@ class ReferentialsController < BreadcrumbController respond_to :js, :only => :show def new + @referential = Referential.new_from(Referential.find(params[:from])) if params[:from] new! do @referential.data_format = current_organisation.data_format end @@ -56,7 +57,11 @@ class ReferentialsController < BreadcrumbController end def create_resource(referential) - referential.organisation = current_organisation + if referential.created_from + referential.clone_association referential.created_from + else + referential.organisation = current_organisation + end super end @@ -74,6 +79,7 @@ class ReferentialsController < BreadcrumbController :projection_type, :data_format, :archived_at, + :created_from_id, referential_metadata_attributes: [:referential_source_id, :line_ids => []] ) end diff --git a/app/models/referential.rb b/app/models/referential.rb index 9612b1022..afadf5edd 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -29,13 +29,14 @@ class Referential < ActiveRecord::Base belongs_to :line_referential validates_presence_of :line_referential + belongs_to :created_from, class_name: 'Referential' has_many :lines, through: :line_referential has_many :companies, through: :line_referential has_many :group_of_lines, through: :line_referential has_many :networks, through: :line_referential - has_one :referential_metadata - accepts_nested_attributes_for :referential_metadata + has_many :referential_metadatas,:dependent => :destroy + accepts_nested_attributes_for :referential_metadatas belongs_to :stop_area_referential validates_presence_of :stop_area_referential @@ -112,6 +113,28 @@ class Referential < ActiveRecord::Base self end + def self.new_from from + Referential.new({ + name: I18n.t("activerecord.copy", :name => from.name), + slug: "#{from.slug}_clone", + prefix: from.prefix, + time_zone: from.time_zone, + bounds: from.bounds, + organisation: from.organisation, + line_referential: from.line_referential, + stop_area_referential: from.stop_area_referential, + workbench: from.workbench, + created_from: from, + }) + end + + def clone_association from + self.organisation = from.organisation + self.line_referential = from.line_referential + self.stop_area_referential = from.stop_area_referential + self.workbench = from.workbench + end + def self.available_srids [ [ "RGF 93 Lambert 93 (2154)", 2154 ], @@ -148,30 +171,44 @@ class Referential < ActiveRecord::Base projection_type || "" end - after_create :autocreate_referential_metadata - def autocreate_referential_metadata - self.create_referential_metadata if workbench + before_validation :assign_line_and_stop_area_referential, :on => :create, if: :workbench + before_create :create_schema + + after_create :create_referential_metadata, if: :workbench, unless: :created_from + after_create :clone_referential_metadatas, if: :created_from + after_create :clone_schema, if: :created_from + + before_destroy :destroy_schema + before_destroy :destroy_jobs + + def create_referential_metadata + self.referential_metadatas.create + end + + def clone_referential_metadatas + self.created_from.referential_metadatas.each do |meta| + self.referential_metadatas << ReferentialMetadata.new_from(meta) + end + self.save + end + + def clone_schema + ReferentialCloning.create(source_referential: self.created_from, target_referential: self) end - before_create :create_schema def create_schema Apartment::Tenant.create slug end - before_validation :assign_line_and_stop_area_referential, :on => :create def assign_line_and_stop_area_referential - if workbench - self.line_referential = workbench.line_referential - self.stop_area_referential = workbench.stop_area_referential - end + self.line_referential = workbench.line_referential + self.stop_area_referential = workbench.stop_area_referential end - before_destroy :destroy_schema def destroy_schema Apartment::Tenant.drop slug end - before_destroy :destroy_jobs def destroy_jobs #Ievkit.delete_jobs(slug) true diff --git a/app/models/referential_cloning.rb b/app/models/referential_cloning.rb new file mode 100644 index 000000000..9bf824ac5 --- /dev/null +++ b/app/models/referential_cloning.rb @@ -0,0 +1,38 @@ +class ReferentialCloning < ActiveRecord::Base + include AASM + belongs_to :source_referential, class_name: 'Referential' + belongs_to :target_referential, class_name: 'Referential' + after_commit :perform_clone, :on => :create + + private + def perform_clone + ReferentialCloningWorker.perform_async(self.id) + end + + aasm column: :status do + state :new, :initial => true + state :pending + state :successful + state :failed + + event :run, after: :update_started_at do + transitions :from => [:new, :failed], :to => :pending + end + + event :successful, after: :update_ended_at do + transitions :from => [:pending, :failed], :to => :successful + end + + event :failed, after: :update_ended_at do + transitions :from => :pending, :to => :failed + end + end + + def update_started_at + update_attribute(:started_at, Time.now) + end + + def update_ended_at + update_attribute(:ended_at, Time.now) + end +end diff --git a/app/models/referential_metadata.rb b/app/models/referential_metadata.rb index 7b1cea301..57ef9f7ca 100644 --- a/app/models/referential_metadata.rb +++ b/app/models/referential_metadata.rb @@ -3,4 +3,12 @@ class ReferentialMetadata < ActiveRecord::Base belongs_to :referential_source, class_name: 'Referential' has_array_of :lines, class_name: 'Chouette::Line' + + def self.new_from from + ReferentialMetadata.new({ + referential_source: from.referential_source, + line_ids: from.line_ids, + periodes: from.periodes + }) + end end diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim index dd426f426..ce53c2ede 100644 --- a/app/views/referentials/_form.html.slim +++ b/app/views/referentials/_form.html.slim @@ -15,8 +15,10 @@ = form.input :upper_corner, input_html: { title: t("formtastic.titles.referential.upper_corner") } = form.input :lower_corner, input_html: { title: t("formtastic.titles.referential.lower_corner") } = form.input :data_format, label: true, include_blank: false + - if @referential.created_from + = form.input :created_from - = form.inputs for: [:referential_metadata, @referential.referential_metadata] do |meta| + = form.inputs for: [:referential_metadatas, @referential.referential_metadatas] do |meta| = meta.inputs :referential_source = form.actions do diff --git a/app/views/offer_workbenches/_referential.html.slim b/app/views/workbenches/_referential.html.slim index cc1964f30..cc1964f30 100644 --- a/app/views/offer_workbenches/_referential.html.slim +++ b/app/views/workbenches/_referential.html.slim diff --git a/app/views/offer_workbenches/show.html.slim b/app/views/workbenches/show.html.slim index 49396a0e2..49396a0e2 100644 --- a/app/views/offer_workbenches/show.html.slim +++ b/app/views/workbenches/show.html.slim diff --git a/app/workers/referential_cloning_worker.rb b/app/workers/referential_cloning_worker.rb new file mode 100644 index 000000000..dda569d7c --- /dev/null +++ b/app/workers/referential_cloning_worker.rb @@ -0,0 +1,24 @@ +class ReferentialCloningWorker + include Sidekiq::Worker + + def perform(id) + # Replace default apartment created schema with clone schema from source referential + ref_cloning = ReferentialCloning.find id + sql_func = "CREATE OR REPLACE FUNCTION clone_schema( source_schema text, dest_schema text, include_recs boolean) RETURNS void AS $BODY$ DECLARE src_oid oid; tbl_oid oid; func_oid oid; object text; buffer text; srctbl text; default_ text; column_ text; qry text; dest_qry text; v_def text; seqval bigint; sq_last_value bigint; sq_max_value bigint; sq_start_value bigint; sq_increment_by bigint; sq_min_value bigint; sq_cache_value bigint; sq_log_cnt bigint; sq_is_called boolean; sq_is_cycled boolean; sq_cycled char(10); BEGIN SELECT oid INTO src_oid FROM pg_namespace WHERE nspname = quote_ident(source_schema); IF NOT FOUND THEN RAISE NOTICE 'source schema % does not exist!', source_schema; RETURN ; END IF; PERFORM nspname FROM pg_namespace WHERE nspname = quote_ident(dest_schema); IF FOUND THEN RAISE NOTICE 'dest schema % already exists!', dest_schema; RETURN ; END IF; EXECUTE 'CREATE SCHEMA ' || quote_ident(dest_schema) ; FOR object IN SELECT sequence_name::text FROM information_schema.sequences WHERE sequence_schema = quote_ident(source_schema) LOOP EXECUTE 'CREATE SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object); srctbl := quote_ident(source_schema) || '.' || quote_ident(object); EXECUTE 'SELECT last_value, max_value, start_value, increment_by, min_value, cache_value, log_cnt, is_cycled, is_called FROM ' || quote_ident(source_schema) || '.' || quote_ident(object) || ';' INTO sq_last_value, sq_max_value, sq_start_value, sq_increment_by, sq_min_value, sq_cache_value, sq_log_cnt, sq_is_cycled, sq_is_called ; IF sq_is_cycled THEN sq_cycled := 'CYCLE'; ELSE sq_cycled := 'NO CYCLE'; END IF; EXECUTE 'ALTER SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object) || ' INCREMENT BY ' || sq_increment_by || ' MINVALUE ' || sq_min_value || ' MAXVALUE ' || sq_max_value || ' START WITH ' || sq_start_value || ' RESTART ' || sq_min_value || ' CACHE ' || sq_cache_value || sq_cycled || ' ;' ; buffer := quote_ident(dest_schema) || '.' || quote_ident(object); IF include_recs THEN EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');' ; ELSE EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_start_value || ', ' || sq_is_called || ');' ; END IF; END LOOP; FOR object IN SELECT TABLE_NAME::text FROM information_schema.tables WHERE table_schema = quote_ident(source_schema) AND table_type = 'BASE TABLE' LOOP buffer := dest_schema || '.' || quote_ident(object); EXECUTE 'CREATE TABLE ' || buffer || '(LIKE ' || quote_ident(source_schema) || '.' || quote_ident(object) || ' INCLUDING ALL)'; IF include_recs THEN EXECUTE 'INSERT INTO ' || buffer || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(object) || ';'; END IF; FOR column_, default_ IN SELECT column_name::text, REPLACE(column_default::text, source_schema, dest_schema) FROM information_schema.COLUMNS WHERE table_schema = dest_schema AND TABLE_NAME = object AND column_default LIKE 'nextval(%' || quote_ident(source_schema) || '%::regclass)' LOOP EXECUTE 'ALTER TABLE ' || buffer || ' ALTER COLUMN ' || column_ || ' SET DEFAULT ' || default_; END LOOP; END LOOP; FOR qry IN SELECT 'ALTER TABLE ' || quote_ident(dest_schema) || '.' || quote_ident(rn.relname) || ' ADD CONSTRAINT ' || quote_ident(ct.conname) || ' ' || pg_get_constraintdef(ct.oid) || ';' FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid WHERE connamespace = src_oid AND rn.relkind = 'r' AND ct.contype = 'f' LOOP EXECUTE qry; END LOOP; FOR object IN SELECT table_name::text, view_definition FROM information_schema.views WHERE table_schema = quote_ident(source_schema) LOOP buffer := dest_schema || '.' || quote_ident(object); SELECT view_definition INTO v_def FROM information_schema.views WHERE table_schema = quote_ident(source_schema) AND table_name = quote_ident(object); EXECUTE 'CREATE OR REPLACE VIEW ' || buffer || ' AS ' || v_def || ';' ; END LOOP; FOR func_oid IN SELECT oid FROM pg_proc WHERE pronamespace = src_oid LOOP SELECT pg_get_functiondef(func_oid) INTO qry; SELECT replace(qry, source_schema, dest_schema) INTO dest_qry; EXECUTE dest_qry; END LOOP; RETURN; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100;" + sql_clone = "SELECT clone_schema('#{ref_cloning.source_referential.slug}', '#{ref_cloning.target_referential.slug}_tmp', TRUE);" + sql_drop = "DROP SCHEMA #{ref_cloning.target_referential.slug} CASCADE;" + sql_rename = "ALTER SCHEMA #{ref_cloning.target_referential.slug}_tmp RENAME TO #{ref_cloning.target_referential.slug};" + ref_cloning.run! + + begin + ActiveRecord::Base.connection.execute sql_func + ActiveRecord::Base.connection.execute sql_clone + ActiveRecord::Base.connection.execute sql_drop + ActiveRecord::Base.connection.execute sql_rename + ref_cloning.successful! + rescue Exception => e + Rails.logger.error "ReferentialCloningWorker : #{e}" + ref_cloning.failed! + end + end +end diff --git a/config/initializers/apartment.rb b/config/initializers/apartment.rb index db352fa6f..7a3549c02 100644 --- a/config/initializers/apartment.rb +++ b/config/initializers/apartment.rb @@ -34,7 +34,9 @@ Apartment.configure do |config| "Chouette::Line", "Chouette::GroupOfLine", "Chouette::Company", - "Chouette::Network" + "Chouette::Network", + "ReferentialCloning", + "Workbench" ] # use postgres schemas? diff --git a/config/routes.rb b/config/routes.rb index 4b4411ea5..842efda84 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,6 +9,7 @@ ChouetteIhm::Application.routes.draw do devise_scope :user do authenticated :user do + mount Sidekiq::Web => '/sidekiq' root :to => 'referentials#index', as: :authenticated_root end diff --git a/db/migrate/20161024123819_add_created_from_to_referentials.rb b/db/migrate/20161024123819_add_created_from_to_referentials.rb new file mode 100644 index 000000000..a125a0c21 --- /dev/null +++ b/db/migrate/20161024123819_add_created_from_to_referentials.rb @@ -0,0 +1,5 @@ +class AddCreatedFromToReferentials < ActiveRecord::Migration + def change + add_reference :referentials, :created_from, index: true + end +end diff --git a/db/migrate/20161024135931_create_referential_clonings.rb b/db/migrate/20161024135931_create_referential_clonings.rb new file mode 100644 index 000000000..d82f30bb1 --- /dev/null +++ b/db/migrate/20161024135931_create_referential_clonings.rb @@ -0,0 +1,13 @@ +class CreateReferentialClonings < ActiveRecord::Migration + def change + create_table :referential_clonings do |t| + t.string :status + t.datetime :started_at + t.datetime :ended_at + t.references :source_referential, index: true + t.references :target_referential, index: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index d54bc86e1..3e877de49 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161020093344) do +ActiveRecord::Schema.define(version: 20161024135931) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -377,6 +377,19 @@ ActiveRecord::Schema.define(version: 20161020093344) do add_index "pt_links", ["objectid"], :name => "pt_links_objectid_key", :unique => true + create_table "referential_clonings", force: true do |t| + t.string "status" + t.datetime "started_at" + t.datetime "ended_at" + t.integer "source_referential_id" + t.integer "target_referential_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "referential_clonings", ["source_referential_id"], :name => "index_referential_clonings_on_source_referential_id" + add_index "referential_clonings", ["target_referential_id"], :name => "index_referential_clonings_on_target_referential_id" + create_table "referential_metadata", force: true do |t| t.integer "referential_id" t.integer "line_ids", array: true @@ -408,8 +421,11 @@ ActiveRecord::Schema.define(version: 20161020093344) do t.integer "stop_area_referential_id" t.integer "workbench_id" t.datetime "archived_at" + t.integer "created_from_id" end + add_index "referentials", ["created_from_id"], :name => "index_referentials_on_created_from_id" + create_table "route_sections", force: true do |t| t.integer "departure_id", limit: 8 t.integer "arrival_id", limit: 8 diff --git a/spec/factories/referential_clonings.rb b/spec/factories/referential_clonings.rb new file mode 100644 index 000000000..e968d44f1 --- /dev/null +++ b/spec/factories/referential_clonings.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :referential_cloning do + association :source_referential, :factory => :referential + association :target_referential, :factory => :referential + end +end diff --git a/spec/features/companies_spec.rb b/spec/features/companies_spec.rb index adb5fa9f9..08221f637 100644 --- a/spec/features/companies_spec.rb +++ b/spec/features/companies_spec.rb @@ -11,8 +11,8 @@ describe "Companies", :type => :feature do describe "list" do it "display companies" do visit line_referential_companies_path(line_referential) - expect(page).to have_content(companies.first.name) - expect(page).to have_content(companies.last.name) + expect(page).to have_content(companies.first.short_name) + expect(page).to have_content(companies.last.short_name) end end diff --git a/spec/models/referential_cloning_spec.rb b/spec/models/referential_cloning_spec.rb new file mode 100644 index 000000000..30391b53e --- /dev/null +++ b/spec/models/referential_cloning_spec.rb @@ -0,0 +1,10 @@ +require 'rails_helper' + +RSpec.describe ReferentialCloning, :type => :model do + it 'should have a valid factory' do + expect(FactoryGirl.build(:referential_cloning)).to be_valid + end + + it { should belong_to :source_referential } + it { should belong_to :target_referential } +end diff --git a/spec/models/referential_spec.rb b/spec/models/referential_spec.rb index f8e6ffacc..c76b157f8 100644 --- a/spec/models/referential_spec.rb +++ b/spec/models/referential_spec.rb @@ -1,12 +1,25 @@ require 'spec_helper' describe Referential, :type => :model do + let(:ref) { create :referential } - it "create a rule_parameter_set" do - referential = create(:referential) + # it "create a rule_parameter_set" do + # referential = create(:referential) #expect(referential.rule_parameter_sets.size).to eq(1) - end + # end - it { should have_one(:referential_metadata) } + it { should have_many(:referential_metadatas) } it { should belong_to(:workbench) } + + context "Cloning referential" do + let(:cloned) { create(:referential, created_from: ref) } + + it 'should create a ReferentialCloning' do + expect { cloned }.to change{ReferentialCloning.count}.by(1) + end + + it 'should clone referential_metadatas' do + expect(cloned.referential_metadatas).not_to be_empty + end + end end diff --git a/spec/workers/referential_cloning_worker_spec.rb b/spec/workers/referential_cloning_worker_spec.rb new file mode 100644 index 000000000..dd7b33f23 --- /dev/null +++ b/spec/workers/referential_cloning_worker_spec.rb @@ -0,0 +1,4 @@ +require 'rails_helper' +RSpec.describe ReferentialCloningWorker, type: :worker do + pending "add some examples to (or delete) #{__FILE__}" +end |
