feat(exports): implement admin export
This commit is contained in:
parent
18eb241e1a
commit
87af7f3261
14 changed files with 218 additions and 12 deletions
|
@ -1,9 +1,10 @@
|
||||||
class Dossiers::ExportComponent < ApplicationComponent
|
class Dossiers::ExportComponent < ApplicationComponent
|
||||||
def initialize(procedure:, exports:, statut: nil, count: nil)
|
def initialize(procedure:, exports:, statut: nil, count: nil, export_url: nil)
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
@exports = exports
|
@exports = exports
|
||||||
@statut = statut
|
@statut = statut
|
||||||
@count = count
|
@count = count
|
||||||
|
@export_url = export_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def exports
|
def exports
|
||||||
|
@ -11,7 +12,7 @@ class Dossiers::ExportComponent < ApplicationComponent
|
||||||
end
|
end
|
||||||
|
|
||||||
def download_export_path(export_format:, force_export: false, no_progress_notification: nil)
|
def download_export_path(export_format:, force_export: false, no_progress_notification: nil)
|
||||||
download_export_instructeur_procedure_path(@procedure,
|
@export_url.call(@procedure,
|
||||||
export_format: export_format,
|
export_format: export_format,
|
||||||
statut: @statut,
|
statut: @statut,
|
||||||
force_export: force_export,
|
force_export: force_export,
|
||||||
|
|
|
@ -4,6 +4,7 @@ module Administrateurs
|
||||||
helper_method :create_archive_url
|
helper_method :create_archive_url
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@exports = Export.find_for_groupe_instructeurs(all_groupe_instructeurs.map(&:id), nil)
|
||||||
@average_dossier_weight = @procedure.average_dossier_weight
|
@average_dossier_weight = @procedure.average_dossier_weight
|
||||||
@count_dossiers_termines_by_month = Traitement.count_dossiers_termines_by_month(all_groupe_instructeurs)
|
@count_dossiers_termines_by_month = Traitement.count_dossiers_termines_by_month(all_groupe_instructeurs)
|
||||||
@archives = Archive.for_groupe_instructeur(all_groupe_instructeurs).to_a
|
@archives = Archive.for_groupe_instructeur(all_groupe_instructeurs).to_a
|
||||||
|
|
70
app/controllers/administrateurs/exports_controller.rb
Normal file
70
app/controllers/administrateurs/exports_controller.rb
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
module Administrateurs
|
||||||
|
class ExportsController < AdministrateurController
|
||||||
|
before_action :retrieve_procedure, only: [:download]
|
||||||
|
|
||||||
|
def download
|
||||||
|
export = Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options)
|
||||||
|
|
||||||
|
if export.ready? && export.old? && force_export?
|
||||||
|
export.destroy
|
||||||
|
export = Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
if export.ready?
|
||||||
|
respond_to do |format|
|
||||||
|
format.turbo_stream do
|
||||||
|
@dossiers_count = export.count
|
||||||
|
assign_exports
|
||||||
|
flash.notice = "L’export au format \"#{export_format}\" est prêt. Vous pouvez le <a href=\"#{export.file.service_url}\">télécharger</a>"
|
||||||
|
end
|
||||||
|
|
||||||
|
format.html do
|
||||||
|
redirect_to export.file.service_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
respond_to do |format|
|
||||||
|
notice_message = "Nous générons cet export. Veuillez revenir dans quelques minutes pour le télécharger."
|
||||||
|
|
||||||
|
format.turbo_stream do
|
||||||
|
@dossiers_count = export.count
|
||||||
|
assign_exports
|
||||||
|
if !params[:no_progress_notification]
|
||||||
|
flash.notice = notice_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
format.html do
|
||||||
|
redirect_to admin_procedure_archives_url(@procedure), notice: notice_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def export_format
|
||||||
|
@export_format ||= params[:export_format]
|
||||||
|
end
|
||||||
|
|
||||||
|
def force_export?
|
||||||
|
@force_export ||= params[:force_export].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def export_options
|
||||||
|
@export_options ||= {
|
||||||
|
time_span_type: params[:time_span_type],
|
||||||
|
statut: params[:statut],
|
||||||
|
procedure_presentation: nil
|
||||||
|
}.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_groupe_instructeurs
|
||||||
|
@procedure.groupe_instructeurs
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_exports
|
||||||
|
@exports = Export.find_for_groupe_instructeurs(all_groupe_instructeurs.map(&:id), nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,7 @@
|
||||||
.container
|
.container
|
||||||
%h1.mb-2
|
%h1.mb-2
|
||||||
Archives
|
Archives
|
||||||
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_admin_procedure_exports_path))
|
||||||
|
|
||||||
= render partial: "shared/archives/notice"
|
= render partial: "shared/archives/notice"
|
||||||
= render partial: "shared/archives/table", locals: {count_dossiers_termines_by_month: @count_dossiers_termines_by_month, archives: @archives, average_dossier_weight: @average_dossier_weight, procedure: @procedure }
|
= render partial: "shared/archives/table", locals: {count_dossiers_termines_by_month: @count_dossiers_termines_by_month, archives: @archives, average_dossier_weight: @average_dossier_weight, procedure: @procedure }
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
- if @can_download_dossiers
|
||||||
|
= turbo_stream.update_all '.procedure-actions' do
|
||||||
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, count: @dossiers_count, export_url: method(:admin_procedure_exports_path))
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
.procedure-actions
|
.procedure-actions
|
||||||
- if @can_download_dossiers
|
- if @can_download_dossiers
|
||||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports)
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_export_instructeur_procedure_path))
|
||||||
|
|
||||||
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
|
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
|
||||||
statut: @statut,
|
statut: @statut,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
- if @can_download_dossiers
|
- if @can_download_dossiers
|
||||||
- if @statut.nil?
|
- if @statut.nil?
|
||||||
= turbo_stream.update_all '.procedure-actions' do
|
= turbo_stream.update_all '.procedure-actions' do
|
||||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports)
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_export_instructeur_procedure_path))
|
||||||
- else
|
- else
|
||||||
= turbo_stream.update_all '.dossiers-export' do
|
= turbo_stream.update_all '.dossiers-export' do
|
||||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count)
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count, export_url: method(:download_export_instructeur_procedure_path))
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
.procedure-actions
|
.procedure-actions
|
||||||
- if @can_download_dossiers
|
- if @can_download_dossiers
|
||||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports)
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_export_instructeur_procedure_path))
|
||||||
|
|
||||||
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
|
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
|
||||||
statut: @statut,
|
statut: @statut,
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
= render partial: "dossiers_filter", locals: { procedure: @procedure, procedure_presentation: @procedure_presentation, current_filters: @current_filters, statut: @statut, filterable_fields_for_select: @filterable_fields_for_select }
|
= render partial: "dossiers_filter", locals: { procedure: @procedure, procedure_presentation: @procedure_presentation, current_filters: @current_filters, statut: @statut, filterable_fields_for_select: @filterable_fields_for_select }
|
||||||
- if @dossiers_count > 0
|
- if @dossiers_count > 0
|
||||||
.dossiers-export
|
.dossiers-export
|
||||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count)
|
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count, export_url: method(:download_export_instructeur_procedure_path))
|
||||||
|
|
||||||
%table.table.dossiers-table.hoverable
|
%table.table.dossiers-table.hoverable
|
||||||
%thead
|
%thead
|
||||||
|
|
|
@ -1,5 +1,25 @@
|
||||||
{
|
{
|
||||||
"ignored_warnings": [
|
"ignored_warnings": [
|
||||||
|
{
|
||||||
|
"warning_type": "Redirect",
|
||||||
|
"warning_code": 18,
|
||||||
|
"fingerprint": "170b506bd3d25eb50f464703f9d993bf2e124d5cbc8478ce9d9d06a15b4bc55e",
|
||||||
|
"check_name": "Redirect",
|
||||||
|
"message": "Possible unprotected redirect",
|
||||||
|
"file": "app/controllers/instructeurs/exports_controller.rb",
|
||||||
|
"line": 26,
|
||||||
|
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
||||||
|
"code": "redirect_to(Export.find_or_create_export(export_format, groupe_instructeurs, **export_options).file.service_url)",
|
||||||
|
"render_path": null,
|
||||||
|
"location": {
|
||||||
|
"type": "method",
|
||||||
|
"class": "Instructeurs::ExportsController",
|
||||||
|
"method": "download"
|
||||||
|
},
|
||||||
|
"user_input": "Export.find_or_create_export(export_format, groupe_instructeurs, **export_options).file.service_url",
|
||||||
|
"confidence": "High",
|
||||||
|
"note": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"warning_type": "Cross-Site Scripting",
|
"warning_type": "Cross-Site Scripting",
|
||||||
"warning_code": 2,
|
"warning_code": 2,
|
||||||
|
@ -112,6 +132,26 @@
|
||||||
"confidence": "High",
|
"confidence": "High",
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"warning_type": "Redirect",
|
||||||
|
"warning_code": 18,
|
||||||
|
"fingerprint": "8f8133f0679f6301e098c4b9c61c2217224126856963abd02cdc5e54efb4e818",
|
||||||
|
"check_name": "Redirect",
|
||||||
|
"message": "Possible unprotected redirect",
|
||||||
|
"file": "app/controllers/administrateurs/exports_controller.rb",
|
||||||
|
"line": 22,
|
||||||
|
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
||||||
|
"code": "redirect_to(Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options).file.service_url)",
|
||||||
|
"render_path": null,
|
||||||
|
"location": {
|
||||||
|
"type": "method",
|
||||||
|
"class": "Administrateurs::ExportsController",
|
||||||
|
"method": "download"
|
||||||
|
},
|
||||||
|
"user_input": "Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options).file.service_url",
|
||||||
|
"confidence": "High",
|
||||||
|
"note": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"warning_type": "SQL Injection",
|
"warning_type": "SQL Injection",
|
||||||
"warning_code": 0,
|
"warning_code": 0,
|
||||||
|
@ -153,6 +193,6 @@
|
||||||
"note": ""
|
"note": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"updated": "2022-06-15 16:15:28 +0200",
|
"updated": "2022-07-08 13:14:19 +0200",
|
||||||
"brakeman_version": "5.2.2"
|
"brakeman_version": "5.2.2"
|
||||||
}
|
}
|
||||||
|
|
7
config/initializers/routing.rb
Normal file
7
config/initializers/routing.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class CustomRoutes
|
||||||
|
def self.load
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
get "/provider_url" => redirect("https://numerique.gouv.fr/"), as: :provider
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -339,6 +339,8 @@ Rails.application.routes.draw do
|
||||||
scope module: 'instructeurs', as: 'instructeur' do
|
scope module: 'instructeurs', as: 'instructeur' do
|
||||||
resources :procedures, only: [:index, :show], param: :procedure_id do
|
resources :procedures, only: [:index, :show], param: :procedure_id do
|
||||||
member do
|
member do
|
||||||
|
resources :archives, only: [:index, :create]
|
||||||
|
|
||||||
resources :groupes, only: [:index, :show], controller: 'groupe_instructeurs' do
|
resources :groupes, only: [:index, :show], controller: 'groupe_instructeurs' do
|
||||||
member do
|
member do
|
||||||
post 'add_instructeur'
|
post 'add_instructeur'
|
||||||
|
@ -396,8 +398,6 @@ Rails.application.routes.draw do
|
||||||
get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
|
get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :archives, only: [:index, :create]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -409,6 +409,13 @@ Rails.application.routes.draw do
|
||||||
scope module: 'administrateurs', path: 'admin', as: 'admin' do
|
scope module: 'administrateurs', path: 'admin', as: 'admin' do
|
||||||
resources :procedures do
|
resources :procedures do
|
||||||
resources :archives, only: [:index, :create]
|
resources :archives, only: [:index, :create]
|
||||||
|
resources :exports, only: [] do
|
||||||
|
collection do
|
||||||
|
get 'download'
|
||||||
|
post 'download'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
collection do
|
collection do
|
||||||
get 'new_from_existing'
|
get 'new_from_existing'
|
||||||
end
|
end
|
||||||
|
|
67
spec/controllers/administrateurs/exports_controller_spec.rb
Normal file
67
spec/controllers/administrateurs/exports_controller_spec.rb
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
describe Administrateurs::ExportsController, type: :controller do
|
||||||
|
describe '#download' do
|
||||||
|
let(:administrateur) { create(:administrateur) }
|
||||||
|
before { sign_in(administrateur.user) }
|
||||||
|
|
||||||
|
subject do
|
||||||
|
get :download, params: { export_format: :csv, procedure_id: procedure.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the procedure does not belongs to admin' do
|
||||||
|
let!(:procedure) { create(:procedure, administrateurs: [create(:administrateur)]) }
|
||||||
|
it 'blocks' do
|
||||||
|
is_expected.to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when admin is allowed' do
|
||||||
|
let!(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||||
|
|
||||||
|
context 'when the export is does not exist' do
|
||||||
|
it 'displays an notice' do
|
||||||
|
is_expected.to redirect_to(admin_procedure_archives_url(procedure))
|
||||||
|
expect(flash.notice).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect { subject }.to change(Export, :count).by(1) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the export is not ready' do
|
||||||
|
before do
|
||||||
|
create(:export, groupe_instructeurs: procedure.groupe_instructeurs)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays an notice' do
|
||||||
|
is_expected.to redirect_to(admin_procedure_archives_url(procedure))
|
||||||
|
expect(flash.notice).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the export is ready' do
|
||||||
|
let(:export) { create(:export, groupe_instructeurs: procedure.groupe_instructeurs) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
export.file.attach(io: StringIO.new('export'), filename: 'file.csv')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays the download link' do
|
||||||
|
subject
|
||||||
|
expect(response.headers['Location']).to start_with("http://test.host/rails/active_storage/disk")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the turbo_stream format is used' do
|
||||||
|
before do
|
||||||
|
post :download,
|
||||||
|
params: { export_format: :csv, procedure_id: procedure.id },
|
||||||
|
format: :turbo_stream
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds in the correct format' do
|
||||||
|
expect(response.media_type).to eq('text/vnd.turbo-stream.html')
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -32,5 +32,13 @@ describe 'Creating a new procedure', js: true do
|
||||||
page.first(".archive-table .button").click
|
page.first(".archive-table .button").click
|
||||||
}.to have_enqueued_job(ArchiveCreationJob).with(procedure, an_instance_of(Archive), administrateur)
|
}.to have_enqueued_job(ArchiveCreationJob).with(procedure, an_instance_of(Archive), administrateur)
|
||||||
expect(page).to have_content("Votre demande a été prise en compte. Selon le nombre de dossiers, cela peut prendre de quelques minutes a plusieurs heures. Vous recevrez un courriel lorsque le fichier sera disponible.")
|
expect(page).to have_content("Votre demande a été prise en compte. Selon le nombre de dossiers, cela peut prendre de quelques minutes a plusieurs heures. Vous recevrez un courriel lorsque le fichier sera disponible.")
|
||||||
|
|
||||||
|
# check exports
|
||||||
|
click_on "Télécharger tous les dossiers"
|
||||||
|
|
||||||
|
expect {
|
||||||
|
click_on "Demander un export au format .xlsx"
|
||||||
|
expect(page).to have_content("Nous générons cet export. Veuillez revenir dans quelques minutes pour le télécharger.")
|
||||||
|
}.to have_enqueued_job(ExportJob).with(an_instance_of(Export))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -120,9 +120,10 @@ describe 'Instructing a dossier:', js: true do
|
||||||
within(:css, '.dossiers-export') do
|
within(:css, '.dossiers-export') do
|
||||||
click_on "Demander un export au format .csv"
|
click_on "Demander un export au format .csv"
|
||||||
end
|
end
|
||||||
expect(page).to have_text('Nous générons cet export.')
|
|
||||||
expect(page).to have_text('Un export au format .csv est en train d’être généré')
|
|
||||||
|
|
||||||
|
expect(page).to have_text('Nous générons cet export.')
|
||||||
|
click_on "Télécharger un dossier"
|
||||||
|
expect(page).to have_text('Un export au format .csv est en train d’être généré')
|
||||||
perform_enqueued_jobs(only: ExportJob)
|
perform_enqueued_jobs(only: ExportJob)
|
||||||
assert_performed_jobs 2
|
assert_performed_jobs 2
|
||||||
page.driver.browser.navigate.refresh
|
page.driver.browser.navigate.refresh
|
||||||
|
|
Loading…
Add table
Reference in a new issue