From d1c3b84ea217bc524faf4b7b5d5ef172360176ee Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Sat, 2 Mar 2024 22:13:09 +0100 Subject: [PATCH] add export template model --- app/models/export_template.rb | 125 ++++++++++++++++++++++++++++ app/models/groupe_instructeur.rb | 1 + app/models/instructeur.rb | 1 + app/models/procedure.rb | 1 + spec/factories/export_template.rb | 34 ++++++++ spec/models/export_template_spec.rb | 76 +++++++++++++++++ 6 files changed, 238 insertions(+) create mode 100644 app/models/export_template.rb create mode 100644 spec/factories/export_template.rb create mode 100644 spec/models/export_template_spec.rb diff --git a/app/models/export_template.rb b/app/models/export_template.rb new file mode 100644 index 000000000..30897d081 --- /dev/null +++ b/app/models/export_template.rb @@ -0,0 +1,125 @@ +class ExportTemplate < ApplicationRecord + include TagsSubstitutionConcern + + belongs_to :groupe_instructeur + has_one :procedure, through: :groupe_instructeur + + DOSSIER_STATE = Dossier.states.fetch(:en_construction) + + def tiptap_default_dossier_directory=(body) + self.content["default_dossier_directory"] = JSON.parse(body) + end + + def tiptap_default_dossier_directory + tiptap_content("default_dossier_directory") + end + + def tiptap_pdf_name=(body) + self.content["pdf_name"] = JSON.parse(body) + end + + def tiptap_pdf_name + tiptap_content("pdf_name") + end + + def attachment_and_path(dossier, attachment, index: 0, row_index: nil) + [ + attachment, + path(dossier, attachment, index, row_index) + ] + end + + def tiptap_convert(dossier, param) + if content[param]["content"]&.first&.[]("content") + render_attributes_for(content[param], dossier) + end + end + + def tiptap_convert_pj(dossier, pj_stable_id) + if content_for_pj_id(pj_stable_id)["content"]&.first["content"] + render_attributes_for(content_for_pj_id(pj_stable_id), dossier) + end + end + + def render_attributes_for(content_for, dossier) + tiptap = TiptapService.new + used_tags = tiptap.used_tags_and_libelle_for(content_for.deep_symbolize_keys) + substitutions = tags_substitutions(used_tags, dossier, escape: false) + tiptap.to_path(content_for.deep_symbolize_keys, substitutions) + end + + + def folder(dossier) + render_attributes_for(content["default_dossier_directory"], dossier) + end + + def export_path(dossier) + File.join(folder(dossier), export_filename(dossier)) + end + + def export_filename(dossier) + "#{render_attributes_for(content["pdf_name"], dossier)}.pdf" + end + + private + + def tiptap_content(key) + content[key]&.to_json + end + + def tiptap_json(prefix) + { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => prefix, "type" => "text" }, { "type" => "mention", "attrs" => DOSSIER_ID_TAG.stringify_keys }] } + ] + } + end + + def content_for_pj_id(stable_id) + content_for_stable_id = content["pjs"].find { _1.symbolize_keys[:stable_id] == stable_id.to_s } + content_for_stable_id.symbolize_keys.fetch(:path) + end + + + def path(dossier, attachment, index, row_index) + if attachment.name == 'pdf_export_for_instructeur' + return export_path(dossier) + end + + dir_path = case attachment.record_type + when 'Dossier' + 'dossier' + when 'Commentaire' + 'messagerie' + when 'Avis' + 'avis' + else + # for attachment + return attachment_path(dossier, attachment, index, row_index) + end + + File.join(folder(dossier), dir_path, attachment.filename.to_s) + end + + def attachment_path(dossier, attachment, index, row_index) + type_de_champ_id = dossier.champs.find(attachment.record_id).type_de_champ_id + stable_id = TypeDeChamp.find(type_de_champ_id).stable_id + tiptap_pj = content["pjs"].find { |pj| pj["stable_id"] == stable_id.to_s } + if tiptap_pj + File.join(folder(dossier), tiptap_convert_pj(dossier, stable_id) + suffix(attachment, index, row_index)) + else + File.join(folder(dossier), "erreur_renommage", attachment.filename.to_s) + end + end + + def suffix(attachment, index, row_index) + suffix = "" + if index >= 1 && !row_index.nil? + suffix += "-#{index + 1}" + suffix += "-#{row_index + 1}" if row_index + end + + suffix + attachment.filename.extension_with_delimiter + end +end diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index b0b165b1e..bd9aad659 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -9,6 +9,7 @@ class GroupeInstructeur < ApplicationRecord has_many :batch_operations, through: :dossiers, source: :batch_operations has_many :assignments, class_name: 'DossierAssignment', dependent: :nullify, inverse_of: :groupe_instructeur has_many :previous_assignments, class_name: 'DossierAssignment', dependent: :nullify, inverse_of: :previous_groupe_instructeur + has_many :export_templates has_and_belongs_to_many :exports, dependent: :destroy has_one :defaut_procedure, -> { with_discarded }, class_name: 'Procedure', foreign_key: :defaut_groupe_instructeur_id, dependent: :nullify, inverse_of: :defaut_groupe_instructeur diff --git a/app/models/instructeur.rb b/app/models/instructeur.rb index 707e4da98..0b724d337 100644 --- a/app/models/instructeur.rb +++ b/app/models/instructeur.rb @@ -14,6 +14,7 @@ class Instructeur < ApplicationRecord has_many :batch_operations, dependent: :nullify has_many :assign_to_with_email_notifications, -> { with_email_notifications }, class_name: 'AssignTo', inverse_of: :instructeur has_many :groupe_instructeur_with_email_notifications, through: :assign_to_with_email_notifications, source: :groupe_instructeur + has_many :export_templates, through: :groupe_instructeurs has_many :commentaires, inverse_of: :instructeur, dependent: :nullify has_many :dossiers, -> { state_not_brouillon }, through: :unordered_groupe_instructeurs diff --git a/app/models/procedure.rb b/app/models/procedure.rb index fe7005599..aa0c5695c 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -153,6 +153,7 @@ class Procedure < ApplicationRecord has_many :administrateurs, through: :administrateurs_procedures, after_remove: -> (procedure, _admin) { procedure.validate! } has_many :groupe_instructeurs, -> { order(:label) }, inverse_of: :procedure, dependent: :destroy has_many :instructeurs, through: :groupe_instructeurs + has_many :export_templates, through: :groupe_instructeurs has_many :active_groupe_instructeurs, -> { active }, class_name: 'GroupeInstructeur', inverse_of: false has_many :closed_groupe_instructeurs, -> { closed }, class_name: 'GroupeInstructeur', inverse_of: false diff --git a/spec/factories/export_template.rb b/spec/factories/export_template.rb new file mode 100644 index 000000000..0f4e8d882 --- /dev/null +++ b/spec/factories/export_template.rb @@ -0,0 +1,34 @@ +FactoryBot.define do + factory :export_template do + name { "Mon export" } + groupe_instructeur + content { + { + "pdf_name" => + { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "export_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_id", "label" => "id dossier" } }, { "text" => " .pdf", "type" => "text" }] } + ] + }, + "default_dossier_directory" => + { + "type" => "doc", + "content" => + [ + { + "type" => "paragraph", + "content" => + [ + { "text" => "dossier_", "type" => "text" }, + { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, + { "text" => " ", "type" => "text" } + ] + } + ] + } + } +} + kind { "zip" } + end +end diff --git a/spec/models/export_template_spec.rb b/spec/models/export_template_spec.rb new file mode 100644 index 000000000..32a3330d5 --- /dev/null +++ b/spec/models/export_template_spec.rb @@ -0,0 +1,76 @@ +describe ExportTemplate do + let(:groupe_instructeur) { create(:groupe_instructeur, procedure:) } + let(:export_template) { build(:export_template, groupe_instructeur:, content:) } + let(:procedure) { create(:procedure_with_dossiers) } + let(:content) do + { + "pdf_name" => { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "mon_export_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }] } + ] + }, + "default_dossier_directory" => { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "DOSSIER_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, { "text" => " ", "type" => "text" }] } + ] + }, + "pjs" => + [ + {path: {"type"=>"doc", "content"=>[{"type"=>"paragraph", "content"=>[{"type"=>"mention", "attrs"=>{"id"=>"dossier_number", "label"=>"numéro du dossier"}}, {"text"=>" _justif", "type"=>"text"}]}]}, stable_id: "3"}, + { path: + {"type"=>"doc", "content"=>[{"type"=>"paragraph", "content"=>[{"text"=>"cni_", "type"=>"text"}, {"type"=>"mention", "attrs"=>{"id"=>"dossier_number", "label"=>"numéro du dossier"}}, {"text"=>" ", "type"=>"text"}]}]}, + stable_id: "5"}, + { path: {"type"=>"doc", "content"=>[{"type"=>"paragraph", "content"=>[{"text"=>"pj_repet_", "type"=>"text"}, {"type"=>"mention", "attrs"=>{"id"=>"dossier_number", "label"=>"numéro du dossier"}}, {"text"=>" ", "type"=>"text"}]}]}, + stable_id: "10"} + ] + } + end + + describe 'new' do + let(:export_template) { build(:export_template, groupe_instructeur: groupe_instructeur) } + let(:procedure) { create(:procedure, types_de_champ_public:) } + let(:types_de_champ_public) do + [ + { type: :integer_number, stable_id: 900 }, + { type: :piece_justificative, libelle: "Justificatif de domicile", mandatory: true, stable_id: 910 } + ] + end + end + + describe '#tiptap_default_dossier_directory' do + it 'returns tiptap_default_dossier_directory from content' do + expect(export_template.tiptap_default_dossier_directory).to eq({ + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "DOSSIER_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, { "text" => " ", "type" => "text" }] } + ] + }.to_json) + end + end + + describe '#tiptap_pdf_name' do + it 'returns tiptap_pdf_name from content' do + expect(export_template.tiptap_pdf_name).to eq({ + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "mon_export_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }] } + ] + }.to_json) + end + end + + describe '#attachment_and_path' do + let(:dossier) { create(:dossier) } + + context 'for export pdf' do + let(:attachment) { double("attachment") } + + it 'gives absolute filename for export of specific dossier' do + allow(attachment).to receive(:name).and_return('pdf_export_for_instructeur') + expect(export_template.attachment_and_path(dossier, attachment)).to eq([attachment, "DOSSIER_#{dossier.id}/mon_export_#{dossier.id}.pdf"]) + end + end + end +end