generate export with export_template
This commit is contained in:
parent
7a39752630
commit
357c07456c
17 changed files with 131 additions and 48 deletions
|
@ -2,7 +2,7 @@ class API::V2::DossiersController < API::V2::BaseController
|
|||
before_action :ensure_dossier_present
|
||||
|
||||
def pdf
|
||||
@acls = PiecesJustificativesService.new(user_profile: Administrateur.new).acl_for_dossier_export(dossier.procedure)
|
||||
@acls = PiecesJustificativesService.new(user_profile: Administrateur.new, export_template: nil).acl_for_dossier_export(dossier.procedure)
|
||||
render(template: 'dossiers/show', formats: [:pdf])
|
||||
end
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ module Instructeurs
|
|||
@is_dossier_in_batch_operation = dossier.batch_operation.present?
|
||||
respond_to do |format|
|
||||
format.pdf do
|
||||
@acls = PiecesJustificativesService.new(user_profile: current_instructeur).acl_for_dossier_export(dossier.procedure)
|
||||
@acls = PiecesJustificativesService.new(user_profile: current_instructeur, export_template: nil).acl_for_dossier_export(dossier.procedure)
|
||||
render(template: 'dossiers/show', formats: [:pdf])
|
||||
end
|
||||
format.all
|
||||
|
|
|
@ -324,13 +324,18 @@ module Instructeurs
|
|||
end
|
||||
|
||||
def export_format
|
||||
@export_format ||= params[:export_format]
|
||||
@export_format ||= params[:export_format].presence || export_template&.kind
|
||||
end
|
||||
|
||||
def export_template
|
||||
@export_template ||= ExportTemplate.find(params[:export_template_id]) if params[:export_template_id].present?
|
||||
end
|
||||
|
||||
def export_options
|
||||
@export_options ||= {
|
||||
time_span_type: params[:time_span_type],
|
||||
statut: params[:statut],
|
||||
export_template:,
|
||||
procedure_presentation: params[:statut].present? ? procedure_presentation : nil
|
||||
}.compact
|
||||
end
|
||||
|
|
|
@ -88,7 +88,7 @@ module Users
|
|||
end
|
||||
|
||||
def show
|
||||
pj_service = PiecesJustificativesService.new(user_profile: current_user)
|
||||
pj_service = PiecesJustificativesService.new(user_profile: current_user, export_template: nil)
|
||||
respond_to do |format|
|
||||
format.pdf do
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
require 'fog/openstack'
|
||||
|
||||
class ActiveStorage::DownloadableFile
|
||||
def self.create_list_from_dossiers(dossiers:, user_profile:)
|
||||
pj_service = PiecesJustificativesService.new(user_profile:)
|
||||
def self.create_list_from_dossiers(dossiers:, user_profile:, export_template: nil)
|
||||
pj_service = PiecesJustificativesService.new(user_profile:, export_template:)
|
||||
|
||||
pj_service.generate_dossiers_export(dossiers) + pj_service.liste_documents(dossiers)
|
||||
end
|
||||
|
|
|
@ -12,6 +12,8 @@ module DownloadManager
|
|||
end
|
||||
|
||||
def download_all
|
||||
# TODO: arriver à enelver ce parametrage d'ActiveStorage
|
||||
ActiveStorage::Current.url_options = { host: ENV.fetch("APP_HOST") }
|
||||
hydra = Typhoeus::Hydra.new(max_concurrency: DOWNLOAD_MAX_PARALLEL)
|
||||
|
||||
attachments.each do |attachment, path|
|
||||
|
|
|
@ -91,6 +91,14 @@ class Champ < ApplicationRecord
|
|||
parent_id.present?
|
||||
end
|
||||
|
||||
def stable_id_with_row
|
||||
[row_id, stable_id].compact
|
||||
end
|
||||
|
||||
def row_index
|
||||
Champ.where(parent:).pluck(:row_id).sort.index(:id)
|
||||
end
|
||||
|
||||
# used for the `required` html attribute
|
||||
# check visibility to avoid hidden required input
|
||||
# which prevent the form from being sent.
|
||||
|
|
|
@ -67,9 +67,10 @@ class Export < ApplicationRecord
|
|||
procedure_presentation_id.present?
|
||||
end
|
||||
|
||||
def self.find_or_create_fresh_export(format, groupe_instructeurs, user_profile, time_span_type: time_span_types.fetch(:everything), statut: statuts.fetch(:tous), procedure_presentation: nil)
|
||||
def self.find_or_create_fresh_export(format, groupe_instructeurs, user_profile, time_span_type: time_span_types.fetch(:everything), statut: statuts.fetch(:tous), procedure_presentation: nil, export_template: nil)
|
||||
attributes = {
|
||||
format:,
|
||||
export_template:,
|
||||
time_span_type:,
|
||||
statut:,
|
||||
key: generate_cache_key(groupe_instructeurs.map(&:id), procedure_presentation)
|
||||
|
@ -148,7 +149,7 @@ class Export < ApplicationRecord
|
|||
end
|
||||
|
||||
def blob
|
||||
service = ProcedureExportService.new(procedure, dossiers_for_export, user_profile)
|
||||
service = ProcedureExportService.new(procedure, dossiers_for_export, user_profile, export_template)
|
||||
|
||||
case format.to_sym
|
||||
when :csv
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
class PiecesJustificativesService
|
||||
def initialize(user_profile:)
|
||||
def initialize(user_profile:, export_template:)
|
||||
@user_profile = user_profile
|
||||
@export_template = export_template
|
||||
end
|
||||
|
||||
def liste_documents(dossiers)
|
||||
|
@ -58,7 +59,11 @@ class PiecesJustificativesService
|
|||
created_at: dossier.updated_at
|
||||
)
|
||||
|
||||
pdfs << ActiveStorage::DownloadableFile.pj_and_path(dossier.id, a)
|
||||
if @export_template
|
||||
pdfs << @export_template.attachment_and_path(dossier, a)
|
||||
else
|
||||
pdfs << ActiveStorage::DownloadableFile.pj_and_path(dossier.id, a)
|
||||
end
|
||||
end
|
||||
|
||||
pdfs
|
||||
|
@ -153,9 +158,14 @@ class PiecesJustificativesService
|
|||
.includes(:blob)
|
||||
.where(record_type: "Champ", record_id: champ_id_dossier_id.keys)
|
||||
.filter { |a| safe_attachment(a) }
|
||||
.map do |a|
|
||||
.map do |a, _i|
|
||||
dossier_id = champ_id_dossier_id[a.record_id]
|
||||
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
|
||||
pj_index = Champ.find(a.record_id).piece_justificative_file.blobs.map(&:id).index(a.blob_id)
|
||||
if @export_template
|
||||
@export_template.attachment_and_path(Dossier.find(dossier_id), a, index: pj_index, row_index: a.record.row_index)
|
||||
else
|
||||
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
class ProcedureExportService
|
||||
attr_reader :procedure, :dossiers
|
||||
|
||||
def initialize(procedure, dossiers, user_profile)
|
||||
def initialize(procedure, dossiers, user_profile, export_template)
|
||||
@procedure = procedure
|
||||
@dossiers = dossiers
|
||||
@user_profile = user_profile
|
||||
@export_template = export_template
|
||||
end
|
||||
|
||||
def to_csv
|
||||
|
@ -36,7 +37,7 @@ class ProcedureExportService
|
|||
end
|
||||
|
||||
def to_zip
|
||||
attachments = ActiveStorage::DownloadableFile.create_list_from_dossiers(dossiers:, user_profile: @user_profile)
|
||||
attachments = ActiveStorage::DownloadableFile.create_list_from_dossiers(dossiers:, user_profile: @user_profile, export_template: @export_template)
|
||||
|
||||
DownloadableFileService.download_and_zip(procedure, attachments, base_filename) do |zip_filepath|
|
||||
ArchiveUploader.new(procedure: procedure, filename: filename(:zip), filepath: zip_filepath).blob
|
||||
|
|
|
@ -121,7 +121,7 @@ describe Experts::AvisController, type: :controller do
|
|||
context 'with a valid avis' do
|
||||
it do
|
||||
service = instance_double(PiecesJustificativesService)
|
||||
expect(PiecesJustificativesService).to receive(:new).with(user_profile: expert).and_return(service)
|
||||
expect(PiecesJustificativesService).to receive(:new).with(user_profile: expert, export_template: nil).and_return(service)
|
||||
expect(service).to receive(:generate_dossiers_export).with(Dossier.where(id: dossier)).and_return([])
|
||||
expect(service).to receive(:liste_documents).with(Dossier.where(id: dossier)).and_return([])
|
||||
is_expected.to have_http_status(:success)
|
||||
|
|
|
@ -936,7 +936,7 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
subject
|
||||
end
|
||||
|
||||
it { expect(assigns(:acls)).to eq(PiecesJustificativesService.new(user_profile: instructeur).acl_for_dossier_export(dossier.procedure)) }
|
||||
it { expect(assigns(:acls)).to eq(PiecesJustificativesService.new(user_profile: instructeur, export_template: nil).acl_for_dossier_export(dossier.procedure)) }
|
||||
it { expect(assigns(:is_dossier_in_batch_operation)).to eq(false) }
|
||||
it { expect(response).to render_template 'dossiers/show' }
|
||||
|
||||
|
|
|
@ -736,6 +736,18 @@ describe Instructeurs::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
it { expect { subject }.to change { Export.where(user_profile: instructeur).count }.by(1) }
|
||||
|
||||
context 'with an export template' do
|
||||
let(:export_template) { create(:export_template) }
|
||||
subject do
|
||||
get :download_export, params: { export_template_id: export_template.id, procedure_id: procedure.id }
|
||||
end
|
||||
|
||||
it 'displays an notice' do
|
||||
is_expected.to redirect_to(exports_instructeur_procedure_url(procedure))
|
||||
expect(flash.notice).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the export is not ready' do
|
||||
|
|
|
@ -1142,7 +1142,7 @@ describe Users::DossiersController, type: :controller do
|
|||
end
|
||||
|
||||
context 'when the dossier has been submitted' do
|
||||
it { expect(assigns(:acls)).to eq(PiecesJustificativesService.new(user_profile: user).acl_for_dossier_export(dossier.procedure)) }
|
||||
it { expect(assigns(:acls)).to eq(PiecesJustificativesService.new(user_profile: user, export_template: nil).acl_for_dossier_export(dossier.procedure)) }
|
||||
it { expect(response).to render_template('dossiers/show') }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -109,6 +109,14 @@ RSpec.describe Export, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with export template' do
|
||||
let(:export_template) { build(:export_template) }
|
||||
it 'creates new export' do
|
||||
expect { Export.find_or_create_fresh_export(:zip, [gi_1], instructeur, export_template: export_template, time_span_type: Export.time_span_types.fetch(:everything), statut: Export.statuts.fetch(:tous), procedure_presentation: pp) }
|
||||
.to change { Export.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with existing matching export' do
|
||||
def find_or_create =
|
||||
Export.find_or_create_fresh_export(:zip, [gi_1], instructeur, time_span_type: Export.time_span_types.fetch(:everything), statut: Export.statuts.fetch(:tous), procedure_presentation: pp)
|
||||
|
|
|
@ -2,8 +2,9 @@ describe PiecesJustificativesService do
|
|||
describe '.liste_documents' do
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:dossiers) { Dossier.where(id: dossier.id) }
|
||||
let(:export_template) { nil }
|
||||
subject do
|
||||
PiecesJustificativesService.new(user_profile:).liste_documents(dossiers).map(&:first)
|
||||
PiecesJustificativesService.new(user_profile:, export_template:).liste_documents(dossiers).map(&:first)
|
||||
end
|
||||
|
||||
context 'no acl' do
|
||||
|
@ -19,6 +20,11 @@ describe PiecesJustificativesService do
|
|||
end
|
||||
|
||||
it { expect(subject).to match_array(pj_champ.call(dossier).piece_justificative_file.attachments) }
|
||||
|
||||
context 'with export_template' do
|
||||
let(:export_template) { create(:export_template, groupe_instructeur: procedure.defaut_groupe_instructeur).tap(&:set_default_values) }
|
||||
it { expect(subject).to match_array(pj_champ.call(dossier).piece_justificative_file.attachments) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a multiple attachments' do
|
||||
|
@ -303,7 +309,7 @@ describe PiecesJustificativesService do
|
|||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :repetition, children: [{ type: :piece_justificative }] }]) }
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, procedure: procedure) }
|
||||
let(:dossiers) { Dossier.where(id: dossier.id) }
|
||||
subject { PiecesJustificativesService.new(user_profile:).generate_dossiers_export(dossiers) }
|
||||
subject { PiecesJustificativesService.new(user_profile:, export_template: nil).generate_dossiers_export(dossiers) }
|
||||
|
||||
it "doesn't update dossier" do
|
||||
expect { subject }.not_to change { dossier.updated_at }
|
||||
|
@ -315,7 +321,7 @@ describe PiecesJustificativesService do
|
|||
let!(:not_confidentiel_avis) { create(:avis, :not_confidentiel, dossier: dossier) }
|
||||
let!(:expert_avis) { create(:avis, :confidentiel, dossier: dossier, expert: user_profile) }
|
||||
|
||||
subject { PiecesJustificativesService.new(user_profile:).generate_dossiers_export(dossiers) }
|
||||
subject { PiecesJustificativesService.new(user_profile:, export_template: nil).generate_dossiers_export(dossiers) }
|
||||
it "includes avis not confidentiel as well as expert's avis" do
|
||||
expect_any_instance_of(Dossier).to receive(:avis_for_expert).with(user_profile).and_return([])
|
||||
subject
|
||||
|
|
|
@ -2,8 +2,9 @@ require 'csv'
|
|||
|
||||
describe ProcedureExportService do
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs, instructeurs: [instructeur]) }
|
||||
let(:service) { ProcedureExportService.new(procedure, procedure.dossiers, instructeur) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs) }
|
||||
let(:service) { ProcedureExportService.new(procedure, procedure.dossiers, instructeur, export_template) }
|
||||
let(:export_template) { nil }
|
||||
|
||||
describe 'to_xlsx' do
|
||||
subject do
|
||||
|
@ -243,7 +244,7 @@ describe ProcedureExportService do
|
|||
|
||||
context 'as csv' do
|
||||
subject do
|
||||
ProcedureExportService.new(procedure, procedure.dossiers, instructeur)
|
||||
ProcedureExportService.new(procedure, procedure.dossiers, instructeur, export_template)
|
||||
.to_csv
|
||||
.open { |f| CSV.read(f.path) }
|
||||
end
|
||||
|
@ -519,39 +520,68 @@ describe ProcedureExportService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'generate_dossiers_export' do
|
||||
describe 'generate_dossiers_export' do
|
||||
it 'include_infos_administration (so it includes avis, champs privés)' do
|
||||
expect(ActiveStorage::DownloadableFile).to receive(:create_list_from_dossiers).with(dossiers: anything, user_profile: instructeur).and_return([])
|
||||
expect(ActiveStorage::DownloadableFile).to receive(:create_list_from_dossiers).with(dossiers: anything, user_profile: instructeur, export_template:).and_return([])
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'with files (and http calls)' do
|
||||
let!(:dossier) { create(:dossier, :accepte, :with_populated_champs, :with_individual, procedure: procedure) }
|
||||
let(:dossier_exports) { PiecesJustificativesService.new(user_profile: instructeur).generate_dossiers_export(Dossier.where(id: dossier)) }
|
||||
before do
|
||||
allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/d0e7862b24d8026a3c03516d865b28151eb3859029c6c6c2e86605891fbdcd7a/socketry/async-io")
|
||||
context 'with export_template' do
|
||||
let!(:dossier) { create(:dossier, :accepte, :with_populated_champs, :with_individual, procedure: procedure) }
|
||||
let(:dossier_exports) { PiecesJustificativesService.new(user_profile: instructeur, export_template:).generate_dossiers_export(Dossier.where(id: dossier)) }
|
||||
let(:export_template) { create(:export_template, groupe_instructeur: procedure.defaut_groupe_instructeur).tap(&:set_default_values) }
|
||||
before do
|
||||
allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/d0e7862b24d8026a3c03516d865b28151eb3859029c6c6c2e86605891fbdcd7a/socketry/async-io")
|
||||
end
|
||||
|
||||
it 'returns a blob with custom filenames' do
|
||||
VCR.use_cassette('archive/new_file_to_get_200') do
|
||||
subject
|
||||
File.write('tmp.zip', subject.download, mode: 'wb')
|
||||
File.open('tmp.zip') do |fd|
|
||||
files = ZipTricks::FileReader.read_zip_structure(io: fd)
|
||||
base_fn = "export"
|
||||
structure = [
|
||||
"#{base_fn}/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/piece_justificative-#{dossier.id}.txt",
|
||||
"#{base_fn}/dossier-#{dossier.id}/export_#{dossier.id}.pdf"
|
||||
]
|
||||
expect(files.size).to eq(structure.size)
|
||||
expect(files.map(&:filename)).to match_array(structure)
|
||||
end
|
||||
FileUtils.remove_entry_secure('tmp.zip')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a blob with valid files' do
|
||||
VCR.use_cassette('archive/new_file_to_get_200') do
|
||||
subject
|
||||
context 'with files (and http calls)' do
|
||||
let!(:dossier) { create(:dossier, :accepte, :with_populated_champs, :with_individual, procedure: procedure) }
|
||||
let(:dossier_exports) { PiecesJustificativesService.new(user_profile: instructeur, export_template: nil).generate_dossiers_export(Dossier.where(id: dossier)) }
|
||||
before do
|
||||
allow_any_instance_of(ActiveStorage::Attachment).to receive(:url).and_return("https://opengraph.githubassets.com/d0e7862b24d8026a3c03516d865b28151eb3859029c6c6c2e86605891fbdcd7a/socketry/async-io")
|
||||
end
|
||||
|
||||
File.write('tmp.zip', subject.download, mode: 'wb')
|
||||
File.open('tmp.zip') do |fd|
|
||||
files = ZipTricks::FileReader.read_zip_structure(io: fd)
|
||||
base_fn = 'export'
|
||||
structure = [
|
||||
"#{base_fn}/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/pieces_justificatives/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/#{ActiveStorage::DownloadableFile.timestamped_filename(ActiveStorage::Attachment.where(record_type: "Champ").first)}",
|
||||
"#{base_fn}/dossier-#{dossier.id}/#{ActiveStorage::DownloadableFile.timestamped_filename(dossier_exports.first.first)}"
|
||||
]
|
||||
expect(files.size).to eq(structure.size)
|
||||
expect(files.map(&:filename)).to match_array(structure)
|
||||
it 'returns a blob with valid files' do
|
||||
VCR.use_cassette('archive/new_file_to_get_200') do
|
||||
subject
|
||||
|
||||
File.write('tmp.zip', subject.download, mode: 'wb')
|
||||
File.open('tmp.zip') do |fd|
|
||||
files = ZipTricks::FileReader.read_zip_structure(io: fd)
|
||||
base_fn = 'export'
|
||||
structure = [
|
||||
"#{base_fn}/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/pieces_justificatives/",
|
||||
"#{base_fn}/dossier-#{dossier.id}/#{ActiveStorage::DownloadableFile.timestamped_filename(ActiveStorage::Attachment.where(record_type: "Champ").first)}",
|
||||
"#{base_fn}/dossier-#{dossier.id}/#{ActiveStorage::DownloadableFile.timestamped_filename(dossier_exports.first.first)}"
|
||||
]
|
||||
expect(files.size).to eq(structure.size)
|
||||
expect(files.map(&:filename)).to match_array(structure)
|
||||
end
|
||||
FileUtils.remove_entry_secure('tmp.zip')
|
||||
end
|
||||
FileUtils.remove_entry_secure('tmp.zip')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue