1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-02 20:15:22 +02:00

Add support for different column separator in csv import logic (#1096)

* add col_sep to import model

* add validation for col_sep column

* add col_sep option to csv import model

* make use of col_sep option in import model

* add column separator field to new/edit action of an import

* add col_sep parameter to create/update action

* fix spacing between fields

Co-authored-by: Zach Gollwitzer <zach.gollwitzer@gmail.com>
Signed-off-by: Alexander Schrot <alexander@axs-labs.com>

---------

Signed-off-by: Alexander Schrot <alexander@axs-labs.com>
Co-authored-by: Zach Gollwitzer <zach.gollwitzer@gmail.com>
This commit is contained in:
Alexander Schrot 2024-08-16 20:00:16 +02:00 committed by GitHub
parent 707c5ca0ca
commit 4527482aa2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 117 additions and 19 deletions

View file

@ -18,14 +18,14 @@ class ImportsController < ApplicationController
def update
account = Current.family.accounts.find(params[:import][:account_id])
@import.update! account: account, col_sep: params[:import][:col_sep]
@import.update! account: account
redirect_to load_import_path(@import), notice: t(".import_updated")
end
def create
account = Current.family.accounts.find(params[:import][:account_id])
@import = Import.create!(account: account)
@import = Import.create! account: account, col_sep: params[:import][:col_sep]
redirect_to load_import_path(@import), notice: t(".import_created")
end

View file

@ -2,6 +2,7 @@ class Import < ApplicationRecord
belongs_to :account
validate :raw_csv_must_be_parsable
validates :col_sep, inclusion: { in: Csv::COL_SEP_LIST }
before_save :initialize_csv, if: :should_initialize_csv?
@ -88,7 +89,7 @@ class Import < ApplicationRecord
def get_raw_csv
return nil if raw_csv_str.nil?
Import::Csv.new(raw_csv_str)
Import::Csv.new(raw_csv_str, col_sep:)
end
def should_initialize_csv?
@ -102,7 +103,7 @@ class Import < ApplicationRecord
# Uses the user-provided raw CSV + mappings to generate a normalized CSV for the import
def generate_normalized_csv(csv_str)
Import::Csv.create_with_field_mappings(csv_str, expected_fields, column_mappings)
Import::Csv.create_with_field_mappings(csv_str, expected_fields, column_mappings, col_sep)
end
def update_csv(row_idx, col_idx, value)
@ -176,7 +177,7 @@ class Import < ApplicationRecord
def raw_csv_must_be_parsable
begin
CSV.parse(raw_csv_str || "")
CSV.parse(raw_csv_str || "", col_sep:)
rescue CSV::MalformedCSVError
# i18n-tasks-use t('activerecord.errors.models.import.attributes.raw_csv_str.invalid_csv_format')
errors.add(:raw_csv_str, :invalid_csv_format)

View file

@ -1,12 +1,20 @@
class Import::Csv
def self.parse_csv(csv_str)
CSV.parse((csv_str || "").strip, headers: true, converters: [ ->(str) { str&.strip } ])
DEFAULT_COL_SEP = ",".freeze
COL_SEP_LIST = [ DEFAULT_COL_SEP, ";" ].freeze
def self.parse_csv(csv_str, col_sep: DEFAULT_COL_SEP)
CSV.parse(
csv_str&.strip || "",
headers: true,
col_sep:,
converters: [ ->(str) { str&.strip } ]
)
end
def self.create_with_field_mappings(raw_csv_str, fields, field_mappings)
raw_csv = self.parse_csv(raw_csv_str)
def self.create_with_field_mappings(raw_csv_str, fields, field_mappings, col_sep = DEFAULT_COL_SEP)
raw_csv = self.parse_csv(raw_csv_str, col_sep:)
generated_csv_str = CSV.generate headers: fields.map { |f| f.key }, write_headers: true do |csv|
generated_csv_str = CSV.generate headers: fields.map { |f| f.key }, write_headers: true, col_sep: do |csv|
raw_csv.each do |row|
row_values = []
@ -22,18 +30,19 @@ class Import::Csv
end
end
new(generated_csv_str)
new(generated_csv_str, col_sep:)
end
attr_reader :csv_str
attr_reader :csv_str, :col_sep
def initialize(csv_str, column_validators: nil)
def initialize(csv_str, column_validators: nil, col_sep: DEFAULT_COL_SEP)
@csv_str = csv_str
@col_sep = col_sep
@column_validators = column_validators || {}
end
def table
@table ||= self.class.parse_csv(csv_str)
@table ||= self.class.parse_csv(csv_str, col_sep:)
end
def update_cell(row_idx, col_idx, value)

View file

@ -1,6 +1,7 @@
<%= styled_form_with model: @import do |form| %>
<div class="mb-4">
<div class="mb-4 space-y-3">
<%= form.collection_select :account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".account"), required: true } %>
<%= form.collection_select :col_sep, Import::Csv::COL_SEP_LIST, :to_s, -> { t(".col_sep_char.#{_1.ord}") }, { prompt: t(".select_col_sep"), label: t(".col_sep"), required: true } %>
</div>
<%= form.submit t(".next"), class: "px-4 py-2 block w-full rounded-lg bg-gray-900 text-white text-sm font-medium cursor-pointer hover:bg-gray-700" %>