diff --git a/Gemfile b/Gemfile index 4a5d5c04c..aea82425d 100644 --- a/Gemfile +++ b/Gemfile @@ -93,6 +93,11 @@ gem 'simple_form' gem 'newrelic_rpm' +# Sidekiq +gem 'sidekiq' +gem 'sidekiq-cron', '~> 0.4.4' +gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git', require: false + gem 'select2-rails' group :test do @@ -136,10 +141,10 @@ group :development, :test do gem 'dotenv-rails' end -group :development, :production, :staging do - gem 'scenic' -end - group :production, :staging do gem 'sentry-raven' end + +group :production, :staging, :development do + gem 'scenic' +end diff --git a/Gemfile.lock b/Gemfile.lock index d102a7b69..3eb20b63b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,6 +7,18 @@ GIT open4 (~> 1.3.4) rake +GIT + remote: https://github.com/sinatra/sinatra.git + revision: d0c4053fd459be9f2c207cfeec5c0606461c014b + specs: + rack-protection (2.0.0.rc1) + rack + sinatra (2.0.0.rc1) + mustermann (= 1.0.0) + rack (~> 2.0) + rack-protection (= 2.0.0.rc1) + tilt (~> 2.0) + GEM remote: https://rubygems.org/ specs: @@ -110,6 +122,7 @@ GEM execjs coffee-script-source (1.11.1) concurrent-ruby (1.0.2) + connection_pool (2.2.1) crack (0.4.3) safe_yaml (~> 1.0.0) database_cleaner (1.5.3) @@ -325,7 +338,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (1.8.3) + json (1.8.6) json-jwt (1.7.0) activesupport bindata @@ -374,6 +387,7 @@ GEM minitest (5.10.1) multi_json (1.12.1) multipart-post (2.0.0) + mustermann (1.0.0) nenv (0.3.0) netrc (0.11.0) newrelic_rpm (3.18.1.330) @@ -460,7 +474,9 @@ GEM nokogiri (~> 1.5) trollop (~> 2.1) rdoc (4.3.0) - redis (3.3.0) + redis (3.3.3) + redis-namespace (1.5.3) + redis (~> 3.0, >= 3.0.4) ref (2.0.0) request_store (1.3.1) responders (2.3.0) @@ -501,6 +517,8 @@ GEM ruby_parser (3.8.3) sexp_processor (~> 4.1) rubyzip (1.0.0) + rufus-scheduler (3.3.4) + tzinfo safe_yaml (1.0.4) sass (3.4.22) sass-rails (5.0.6) @@ -524,6 +542,15 @@ GEM shellany (0.0.1) shoulda-matchers (3.1.1) activesupport (>= 4.0.0) + sidekiq (4.2.9) + concurrent-ruby (~> 1.0) + connection_pool (~> 2.2, >= 2.2.0) + rack-protection (>= 1.5.0) + redis (~> 3.2, >= 3.2.1) + sidekiq-cron (0.4.5) + redis-namespace (>= 1.5.2) + rufus-scheduler (>= 2.0.24) + sidekiq (>= 4.2.1) simple_form (3.4.0) actionpack (> 4, < 5.1) activemodel (> 4, < 5.1) @@ -669,7 +696,10 @@ DEPENDENCIES select2-rails sentry-raven shoulda-matchers + sidekiq + sidekiq-cron (~> 0.4.4) simple_form + sinatra! smart_listing spreadsheet_architect spring diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index fa04bb72d..639c79242 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -193,10 +193,11 @@ class Admin::ProceduresController < AdminController private def procedure_params + editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :lien_notice, :euro_flag, :logo, :auto_archive_on] if @procedure.try(:locked?) - params.require(:procedure).permit(:libelle, :description, :organisation, :direction, :lien_site_web, :lien_notice, :euro_flag, :logo) + params.require(:procedure).permit(*editable_params) else - params.require(:procedure).permit(:libelle, :description, :organisation, :direction, :lien_site_web, :lien_notice, :euro_flag, :logo, :lien_demarche, :cerfa_flag, :for_individual, :individual_with_siret, module_api_carto_attributes: [:id, :use_api_carto, :quartiers_prioritaires, :cadastre]).merge(administrateur_id: current_administrateur.id) + params.require(:procedure).permit(*editable_params, :lien_demarche, :cerfa_flag, :for_individual, :individual_with_siret, module_api_carto_attributes: [:id, :use_api_carto, :quartiers_prioritaires, :cadastre]).merge(administrateur_id: current_administrateur.id) end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 00afd691e..7002b2ab9 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -181,9 +181,7 @@ class Dossier < ActiveRecord::Base where(state: WAITING_FOR_USER, archived: false).order("updated_at #{order}") end - def self.en_construction order = 'ASC' - where(state: EN_CONSTRUCTION, archived: false).order("updated_at #{order}") - end + scope :en_construction, -> { where(state: EN_CONSTRUCTION, archived: false).order(updated_at: :asc) } def self.ouvert order = 'ASC' where(state: OUVERT, archived: false).order("updated_at #{order}") diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 304c459dc..1d791e9d8 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -46,6 +46,8 @@ class Procedure < ActiveRecord::Base alias_method_chain "#{name.underscore.to_sym}".to_s, :override end + scope :not_archived, -> { where(archived: false) } + def path procedure_path.path unless procedure_path.nil? end @@ -66,10 +68,6 @@ class Procedure < ActiveRecord::Base types_de_piece_justificative.order(:order_place) end - def self.not_archived id - Procedure.where(archived: false).find(id) - end - def self.active id Procedure.where(archived: false, published: true).find(id) end diff --git a/app/views/admin/procedures/_informations.html.haml b/app/views/admin/procedures/_informations.html.haml index 833f5fee3..c379ac924 100644 --- a/app/views/admin/procedures/_informations.html.haml +++ b/app/views/admin/procedures/_informations.html.haml @@ -80,3 +80,16 @@ %label = f.check_box :individual_with_siret Donner la possibilité de renseigner un SIRET au cours de la construction du dossier. +.row + .col-md-6 + %h4 Options avancées + + %label{ for: :auto_archive_on} Archivage automatique le + = f.text_field :auto_archive_on, id: 'auto_archive_on', value: @procedure.auto_archive_on.try{ |d| d.strftime("%d-%m-%Y") }, data: { provide: 'datepicker', 'date-format' => 'dd/mm/yyyy' } + (à 00h00) + %p.help-block + %i.fa.fa-info-circle + L'archivage automatique de la procédure entrainera le passage en instruction de tous les dossiers en construction. + + + diff --git a/app/workers/auto_archive_procedure_worker.rb b/app/workers/auto_archive_procedure_worker.rb new file mode 100644 index 000000000..61701b4ff --- /dev/null +++ b/app/workers/auto_archive_procedure_worker.rb @@ -0,0 +1,14 @@ +class AutoArchiveProcedureWorker + include Sidekiq::Worker + + def perform(*args) + procedures_to_archive = Procedure.not_archived.where("auto_archive_on <= ?", Date.today) + + procedures_to_archive.each do |p| + p.dossiers.en_construction.update_all(state: :received) + end + + procedures_to_archive.update_all(archived: true, auto_archive_on: nil) + + end +end diff --git a/config/deploy.rb b/config/deploy.rb index d6aa94452..bc9960d27 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -78,6 +78,7 @@ set :shared_paths, [ "config/fog_credentials.yml", 'config/initializers/secret_token.rb', 'config/initializers/features.yml', + 'config/initializers/sidekiq.rb', "config/environments/#{rails_env}.rb", "config/initializers/token.rb", "config/initializers/urls.rb", diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb new file mode 100644 index 000000000..f5dd670d6 --- /dev/null +++ b/config/initializers/sidekiq.rb @@ -0,0 +1,7 @@ +Sidekiq.configure_server do |config| + Sidekiq::Logging.logger = Rails.logger + + schedule_file = "config/schedule.yml" + Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) +end + diff --git a/config/routes.rb b/config/routes.rb index 716893f09..5811b4890 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,9 +42,15 @@ Rails.application.routes.draw do get 'admin' => 'admin#index' get 'backoffice' => 'backoffice#index' - resources :administrations, only: [:index, :create] - namespace :administrations do - resources :stats, only: [:index] + authenticate :administration do + resources :administrations, only: [:index, :create] + namespace :administrations do + resources :stats, only: [:index] + + require 'sidekiq/web' + require 'sidekiq/cron/web' + mount Sidekiq::Web => '/sidekiq' + end end namespace :france_connect do @@ -208,6 +214,4 @@ Rails.application.routes.draw do end apipie - - mount ActionCable.server => '/cable' end diff --git a/config/schedule.yml b/config/schedule.yml new file mode 100644 index 000000000..79dbdefc9 --- /dev/null +++ b/config/schedule.yml @@ -0,0 +1,3 @@ +auto_archive_procedure: + cron: "* * * * *" + class: "AutoArchiveProcedureWorker" diff --git a/config/sidekiq.yml b/config/sidekiq.yml new file mode 100644 index 000000000..9431fe006 --- /dev/null +++ b/config/sidekiq.yml @@ -0,0 +1,7 @@ +:concurrency: 5 +staging: + :concurrency: 2 +production: + :concurrency: 2 +:queues: + - default \ No newline at end of file diff --git a/db/migrate/20170313140834_add_auto_archive_to_procedure.rb b/db/migrate/20170313140834_add_auto_archive_to_procedure.rb new file mode 100644 index 000000000..7d7275651 --- /dev/null +++ b/db/migrate/20170313140834_add_auto_archive_to_procedure.rb @@ -0,0 +1,5 @@ +class AddAutoArchiveToProcedure < ActiveRecord::Migration[5.0] + def change + add_column :procedures, :auto_archive_on, :date + end +end diff --git a/db/schema.rb b/db/schema.rb index db86ebfaf..0ff0aa06a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170307092820) do +ActiveRecord::Schema.define(version: 20170313140834) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -344,6 +344,7 @@ ActiveRecord::Schema.define(version: 20170307092820) do t.string "lien_notice" t.boolean "for_individual", default: false t.boolean "individual_with_siret", default: false + t.date "auto_archive_on" end create_table "quartier_prioritaires", force: :cascade do |t| diff --git a/spec/models/drop_down_list_spec.rb b/spec/models/drop_down_list_spec.rb index be53e9dea..0455985fc 100644 --- a/spec/models/drop_down_list_spec.rb +++ b/spec/models/drop_down_list_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe DropDownList do + describe 'database columns' do it { is_expected.to have_db_column(:value) } end diff --git a/spec/workers/auto_archive_procedure_worker_spec.rb b/spec/workers/auto_archive_procedure_worker_spec.rb new file mode 100644 index 000000000..e67a19133 --- /dev/null +++ b/spec/workers/auto_archive_procedure_worker_spec.rb @@ -0,0 +1,78 @@ +require 'rails_helper' + +RSpec.describe AutoArchiveProcedureWorker, type: :worker do + + let!(:procedure) { create(:procedure, archived: false, auto_archive_on: nil )} + let!(:procedure_hier) { create(:procedure, archived: false, auto_archive_on: 1.day.ago )} + let!(:procedure_aujourdhui) { create(:procedure, archived: false, auto_archive_on: Date.today )} + let!(:procedure_demain) { create(:procedure, archived: false, auto_archive_on: 1.day.from_now )} + + subject { AutoArchiveProcedureWorker.new.perform } + + context "when procedures have no auto_archive_on" do + + before do + subject + procedure.reload + end + + it { expect(procedure.archived).to eq false } + + end + + context "when procedures have auto_archive_on set on yesterday or today" do + + describe "titi" do + before do + subject + procedure_hier.reload + procedure_aujourdhui.reload + end + + it { expect(procedure_hier.archived).to eq true } + it { expect(procedure_aujourdhui.archived).to eq true } + + end + + + context "with dossiers" do + + let!(:dossier1) { create(:dossier, procedure: procedure_hier, state: 'draft', archived: false)} + let!(:dossier2) { create(:dossier, procedure: procedure_hier, state: 'initiated', archived: false)} + let!(:dossier3) { create(:dossier, procedure: procedure_hier, state: 'replied', archived: false)} + let!(:dossier4) { create(:dossier, procedure: procedure_hier, state: 'updated', archived: false)} + let!(:dossier5) { create(:dossier, procedure: procedure_hier, state: 'received', archived: false)} + let!(:dossier6) { create(:dossier, procedure: procedure_hier, state: 'closed', archived: false)} + let!(:dossier7) { create(:dossier, procedure: procedure_hier, state: 'refused', archived: false)} + let!(:dossier8) { create(:dossier, procedure: procedure_hier, state: 'without_continuation', archived: false)} + + before do + subject + (1..8).each do |i| + eval "dossier#{i}.reload" + end + end + + it { expect(dossier1.state).to eq 'draft' } + it { expect(dossier2.state).to eq 'received' } + it { expect(dossier3.state).to eq 'received' } + it { expect(dossier4.state).to eq 'received' } + it { expect(dossier5.state).to eq 'received' } + it { expect(dossier6.state).to eq 'closed' } + it { expect(dossier7.state).to eq 'refused' } + it { expect(dossier8.state).to eq 'without_continuation' } + + end + end + + context "when procedures have auto_archive_on set on future" do + + before do + subject + end + + it { expect(procedure_demain.archived).to eq false } + + end + +end