From 977cfc87ce24f0eca4620f882f0998def5f3dbe4 Mon Sep 17 00:00:00 2001 From: simon lehericey Date: Thu, 18 Jul 2024 09:57:23 +0200 Subject: [PATCH] Add export_item type --- app/models/export_item.rb | 70 +++++++++++++++++++++++++++++ app/types/export_item_type.rb | 40 +++++++++++++++++ config/initializers/types.rb | 5 +++ spec/models/export_item_spec.rb | 20 +++++++++ spec/types/export_item_type_spec.rb | 55 +++++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 app/models/export_item.rb create mode 100644 app/types/export_item_type.rb create mode 100644 config/initializers/types.rb create mode 100644 spec/models/export_item_spec.rb create mode 100644 spec/types/export_item_type_spec.rb diff --git a/app/models/export_item.rb b/app/models/export_item.rb new file mode 100644 index 000000000..8ee6e4323 --- /dev/null +++ b/app/models/export_item.rb @@ -0,0 +1,70 @@ +class ExportItem + include TagsSubstitutionConcern + DOSSIER_STATE = Dossier.states.fetch(:en_construction) + FORMAT_DATE = "%Y-%m-%d".freeze + + attr_reader :template, :enabled, :stable_id + + def initialize(template:, enabled: true, stable_id: nil) + @template, @enabled, @stable_id = template, enabled, stable_id + end + + def self.default(prefix:, enabled: true, stable_id: nil) + new(template: prefix_dossier_id(prefix), enabled:, stable_id:) + end + + def self.default_pj(tdc) + default(prefix: tdc.libelle_as_filename, enabled: false, stable_id: tdc.stable_id) + end + + def enabled? = enabled + + def template_json = template.to_json + + def template_string = TiptapService.new.to_path(template) + + def path(dossier, attachment: nil, row_index: nil, index: nil) + used_tags = TiptapService.used_tags_and_libelle_for(template) + substitutions = tags_substitutions(used_tags, dossier, escape: false, memoize: true) + substitutions['original-filename'] = attachment.filename.base if attachment + + TiptapService.new.to_path(template, substitutions) + suffix(attachment, row_index, index) + end + + def ==(other) + self.class == other.class && + template == other.template && + enabled == other.enabled && + stable_id == other.stable_id + end + + private + + def self.prefix_dossier_id(prefix) + { + type: "doc", + content: [ + { + type: "paragraph", + content: [ + { text: "#{prefix}-", type: "text" }, + { type: "mention", attrs: DOSSIER_ID_TAG.slice(:id, :label) } + ] + } + ] + } + end + + def suffix(attachment, row_index, index) + suffix = "" + suffix += "-#{add_one_and_pad(row_index)}" if row_index.present? + suffix += "-#{add_one_and_pad(index)}" if index.present? + suffix += attachment.filename.extension_with_delimiter if attachment + + suffix + end + + def add_one_and_pad(number) + (number + 1).to_s.rjust(2, '0') if number.present? + end +end diff --git a/app/types/export_item_type.rb b/app/types/export_item_type.rb new file mode 100644 index 000000000..182941eef --- /dev/null +++ b/app/types/export_item_type.rb @@ -0,0 +1,40 @@ +class ExportItemType < ActiveRecord::Type::Value + # form_input, or setter -> type + def cast(value) + value = value.deep_symbolize_keys if value.respond_to?(:deep_symbolize_keys) + + case value + in ExportItem + value + in NilClass # default value + nil + # from db + in { template: Hash, enabled: TrueClass | FalseClass } => h + + ExportItem.new(**h.slice(:template, :enabled, :stable_id)) + # from form + in { template: String } => h + + template = JSON.parse(h[:template]).deep_symbolize_keys + enabled = h[:enabled] == 'true' + stable_id = h[:stable_id]&.to_i + ExportItem.new(template:, enabled:, stable_id:) + end + end + + # db -> ruby + def deserialize(value) = cast(value&.then { JSON.parse(_1) }) + + # ruby -> db + def serialize(value) + if value.is_a?(ExportItem) + JSON.generate({ + template: value.template, + enabled: value.enabled, + stable_id: value.stable_id + }.compact) + else + raise ArgumentError, "Invalid value for ExportItem serialization: #{value}" + end + end +end diff --git a/config/initializers/types.rb b/config/initializers/types.rb new file mode 100644 index 000000000..edf12e317 --- /dev/null +++ b/config/initializers/types.rb @@ -0,0 +1,5 @@ +require Rails.root.join("app/types/export_item_type") + +ActiveSupport.on_load(:active_record) do + ActiveRecord::Type.register(:export_item, ExportItemType) +end diff --git a/spec/models/export_item_spec.rb b/spec/models/export_item_spec.rb new file mode 100644 index 000000000..05c3cd41a --- /dev/null +++ b/spec/models/export_item_spec.rb @@ -0,0 +1,20 @@ +describe ExportItem do + describe 'path' do + let(:export_item) { ExportItem.default(prefix: 'custom') } + let(:dossier) { create(:dossier) } + let(:attachment) do + ActiveStorage::Attachment.new( + name: 'filename', + blob: ActiveStorage::Blob.new(filename: "file.pdf") + ) + end + + context 'without index nor row_index' do + it do + expect(export_item.path(dossier, attachment:)).to eq("custom-#{dossier.id}.pdf") + expect(export_item.path(dossier, attachment:, index: 3)).to eq("custom-#{dossier.id}-04.pdf") + expect(export_item.path(dossier, attachment:, row_index: 2, index: 3)).to eq("custom-#{dossier.id}-03-04.pdf") + end + end + end +end diff --git a/spec/types/export_item_type_spec.rb b/spec/types/export_item_type_spec.rb new file mode 100644 index 000000000..1a0d270b0 --- /dev/null +++ b/spec/types/export_item_type_spec.rb @@ -0,0 +1,55 @@ +describe ExportItemType do + let(:type) { ExportItemType.new } + + describe 'cast' do + it 'from ExportItem' do + export_item = ExportItem.new(template: { foo: 'bar' }, enabled: true, stable_id: 42) + expect(type.cast(export_item)).to eq(export_item) + end + + it 'from nil' do + expect(type.cast(nil)).to eq(nil) + end + + it 'from db' do + h = { template: { foo: 'bar' }, enabled: true, stable_id: 42 } + expect(type.cast(h)).to eq(ExportItem.new(template: { foo: 'bar' }, enabled: true, stable_id: 42)) + end + + it 'from form' do + h = { template: '{"foo":{"bar":"zob"}}' } + expect(type.cast(h)).to eq(ExportItem.new(template: { foo: { bar: 'zob' } }, enabled: false)) + + h = { template: '{"foo":{"bar":"zob"}}', enabled: 'true' } + expect(type.cast(h)).to eq(ExportItem.new(template: { foo: { bar: 'zob' } }, enabled: true)) + + h = { template: '{"foo":{"bar":"zob"}}', stable_id: '42' } + expect(type.cast(h)).to eq(ExportItem.new(template: { foo: { bar: 'zob' } }, enabled: false, stable_id: 42)) + + h = { template: '{"foo":{"bar":"zob"}}', enabled: 'true', stable_id: '42' } + expect(type.cast(h)).to eq(ExportItem.new(template: { foo: { bar: 'zob' } }, enabled: true, stable_id: 42)) + end + + it 'from invalid value' do + expect { type.cast('invalid value') }.to raise_error(NoMatchingPatternError) + end + end + + describe 'deserialize' do + it 'from nil' do + expect(type.deserialize(nil)).to eq(nil) + end + + it 'from db' do + h = { template: { foo: 'bar' }, enabled: true, stable_id: 42 } + expect(type.deserialize(JSON.generate(h))).to eq(ExportItem.new(template: { foo: 'bar' }, enabled: true, stable_id: 42)) + end + end + + describe 'serialize' do + it 'from ExportItem' do + export_item = ExportItem.new(template: { foo: 'bar' }, enabled: true, stable_id: 42) + expect(type.serialize(export_item)).to eq('{"template":{"foo":"bar"},"enabled":true,"stable_id":42}') + end + end +end