refactor: Reduce function complexity and nesting depth
- Extract helper functions from process_chunk to reduce nesting - Extract format_error_message from extract_changeset_error - Split extract_error_message into smaller functions to reduce complexity - Fixes Credo refactoring opportunities
This commit is contained in:
parent
0abcf540bb
commit
433f008af8
2 changed files with 107 additions and 55 deletions
|
|
@ -302,28 +302,15 @@ defmodule Mv.Membership.Import.MemberCSV do
|
||||||
max_errors = Keyword.get(opts, :max_errors, 50)
|
max_errors = Keyword.get(opts, :max_errors, 50)
|
||||||
|
|
||||||
{inserted, failed, errors, _collected_error_count, truncated?} =
|
{inserted, failed, errors, _collected_error_count, truncated?} =
|
||||||
Enum.reduce(chunk_rows_with_lines, {0, 0, [], 0, false}, fn {line_number, row_map},
|
Enum.reduce(chunk_rows_with_lines, {0, 0, [], 0, false}, fn {line_number, row_map}, acc ->
|
||||||
{acc_inserted, acc_failed,
|
current_error_count = existing_error_count + elem(acc, 3)
|
||||||
acc_errors, acc_error_count,
|
|
||||||
acc_truncated?} ->
|
|
||||||
current_error_count = existing_error_count + acc_error_count
|
|
||||||
|
|
||||||
case process_row(row_map, line_number, custom_field_lookup) do
|
case process_row(row_map, line_number, custom_field_lookup) do
|
||||||
{:ok, _member} ->
|
{:ok, _member} ->
|
||||||
{acc_inserted + 1, acc_failed, acc_errors, acc_error_count, acc_truncated?}
|
update_inserted(acc)
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
new_acc_failed = acc_failed + 1
|
handle_row_error(acc, error, current_error_count, max_errors)
|
||||||
|
|
||||||
# Only collect errors if under limit
|
|
||||||
{new_acc_errors, new_error_count, new_truncated?} =
|
|
||||||
if current_error_count < max_errors do
|
|
||||||
{[error | acc_errors], acc_error_count + 1, acc_truncated?}
|
|
||||||
else
|
|
||||||
{acc_errors, acc_error_count, true}
|
|
||||||
end
|
|
||||||
|
|
||||||
{acc_inserted, new_acc_failed, new_acc_errors, new_error_count, new_truncated?}
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -397,11 +384,9 @@ defmodule Mv.Membership.Import.MemberCSV do
|
||||||
|
|
||||||
# Extracts the first error from a changeset and converts it to a MemberCSV.Error struct
|
# Extracts the first error from a changeset and converts it to a MemberCSV.Error struct
|
||||||
defp extract_changeset_error(changeset, csv_line_number) do
|
defp extract_changeset_error(changeset, csv_line_number) do
|
||||||
case Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
|
errors = Ecto.Changeset.traverse_errors(changeset, &format_error_message/1)
|
||||||
Enum.reduce(opts, msg, fn {key, value}, acc ->
|
|
||||||
String.replace(acc, "%{#{key}}", to_string(value))
|
case errors do
|
||||||
end)
|
|
||||||
end) do
|
|
||||||
%{email: [message | _]} ->
|
%{email: [message | _]} ->
|
||||||
# Email-specific error
|
# Email-specific error
|
||||||
%Error{
|
%Error{
|
||||||
|
|
@ -430,6 +415,56 @@ defmodule Mv.Membership.Import.MemberCSV do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Helper function to update accumulator when row is successfully inserted
|
||||||
|
defp update_inserted({acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?}) do
|
||||||
|
{acc_inserted + 1, acc_failed, acc_errors, acc_error_count, acc_truncated?}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper function to handle row error with error count limit checking
|
||||||
|
defp handle_row_error(
|
||||||
|
{acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?},
|
||||||
|
error,
|
||||||
|
current_error_count,
|
||||||
|
max_errors
|
||||||
|
) do
|
||||||
|
new_acc_failed = acc_failed + 1
|
||||||
|
|
||||||
|
{new_acc_errors, new_error_count, new_truncated?} =
|
||||||
|
collect_error_if_under_limit(
|
||||||
|
error,
|
||||||
|
acc_errors,
|
||||||
|
acc_error_count,
|
||||||
|
acc_truncated?,
|
||||||
|
current_error_count,
|
||||||
|
max_errors
|
||||||
|
)
|
||||||
|
|
||||||
|
{acc_inserted, new_acc_failed, new_acc_errors, new_error_count, new_truncated?}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper function to collect error only if under limit
|
||||||
|
defp collect_error_if_under_limit(
|
||||||
|
error,
|
||||||
|
acc_errors,
|
||||||
|
acc_error_count,
|
||||||
|
acc_truncated?,
|
||||||
|
current_error_count,
|
||||||
|
max_errors
|
||||||
|
) do
|
||||||
|
if current_error_count < max_errors do
|
||||||
|
{[error | acc_errors], acc_error_count + 1, acc_truncated?}
|
||||||
|
else
|
||||||
|
{acc_errors, acc_error_count, true}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Formats error message by replacing placeholders
|
||||||
|
defp format_error_message({msg, opts}) do
|
||||||
|
Enum.reduce(opts, msg, fn {key, value}, acc ->
|
||||||
|
String.replace(acc, "%{#{key}}", to_string(value))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
# Maps changeset error messages to appropriate Gettext messages
|
# Maps changeset error messages to appropriate Gettext messages
|
||||||
defp gettext_error_message(message) when is_binary(message) do
|
defp gettext_error_message(message) when is_binary(message) do
|
||||||
cond do
|
cond do
|
||||||
|
|
|
||||||
|
|
@ -355,48 +355,65 @@ defmodule MvWeb.MemberLive.Form do
|
||||||
|
|
||||||
# Extracts a user-friendly error message from form errors
|
# Extracts a user-friendly error message from form errors
|
||||||
defp extract_error_message(form) do
|
defp extract_error_message(form) do
|
||||||
# Try to extract message from source errors first
|
|
||||||
source_errors = get_source_errors(form)
|
source_errors = get_source_errors(form)
|
||||||
|
|
||||||
case source_errors do
|
cond do
|
||||||
[%Ash.Error.Invalid{errors: errors} | _] when is_list(errors) ->
|
has_invalid_error?(source_errors) ->
|
||||||
# Extract first error message
|
extract_invalid_error_message(source_errors)
|
||||||
case List.first(errors) do
|
|
||||||
%{message: message} when is_binary(message) ->
|
|
||||||
gettext("Validation failed: %{message}", message: message)
|
|
||||||
|
|
||||||
%{field: field, message: message} when is_binary(message) ->
|
has_other_error?(source_errors) ->
|
||||||
gettext("Validation failed: %{field} %{message}", field: field, message: message)
|
extract_other_error_message(source_errors)
|
||||||
|
|
||||||
_ ->
|
has_form_errors?(form) ->
|
||||||
gettext("Validation failed. Please check your input.")
|
gettext("Please correct the errors in the form and try again.")
|
||||||
end
|
|
||||||
|
|
||||||
[error | _] ->
|
true ->
|
||||||
# Try to extract message from other error types
|
gettext("Failed to save member. Please try again.")
|
||||||
case error do
|
end
|
||||||
%{message: message} when is_binary(message) ->
|
end
|
||||||
message
|
|
||||||
|
|
||||||
error when is_struct(error) ->
|
# Checks if source errors contain an Ash.Error.Invalid
|
||||||
# Try to use Ash.ErrorKind protocol if available
|
defp has_invalid_error?([%Ash.Error.Invalid{errors: errors} | _]) when is_list(errors), do: true
|
||||||
try do
|
defp has_invalid_error?(_), do: false
|
||||||
Ash.ErrorKind.message(error)
|
|
||||||
rescue
|
|
||||||
Protocol.UndefinedError -> gettext("Failed to save member. Please try again.")
|
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
# Extracts message from Ash.Error.Invalid
|
||||||
gettext("Failed to save member. Please try again.")
|
defp extract_invalid_error_message([%Ash.Error.Invalid{errors: errors} | _]) do
|
||||||
end
|
case List.first(errors) do
|
||||||
|
%{message: message} when is_binary(message) ->
|
||||||
|
gettext("Validation failed: %{message}", message: message)
|
||||||
|
|
||||||
|
%{field: field, message: message} when is_binary(message) ->
|
||||||
|
gettext("Validation failed: %{field} %{message}", field: field, message: message)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
# Check if there are any field errors in the form
|
gettext("Validation failed. Please check your input.")
|
||||||
if has_form_errors?(form) do
|
end
|
||||||
gettext("Please correct the errors in the form and try again.")
|
end
|
||||||
else
|
|
||||||
gettext("Failed to save member. Please try again.")
|
# Checks if source errors contain other error types
|
||||||
end
|
defp has_other_error?([_ | _]), do: true
|
||||||
|
defp has_other_error?(_), do: false
|
||||||
|
|
||||||
|
# Extracts message from other error types
|
||||||
|
defp extract_other_error_message([error | _]) do
|
||||||
|
cond do
|
||||||
|
Map.has_key?(error, :message) and is_binary(error.message) ->
|
||||||
|
error.message
|
||||||
|
|
||||||
|
is_struct(error) ->
|
||||||
|
extract_struct_error_message(error)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
gettext("Failed to save member. Please try again.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extracts message from struct error using Ash.ErrorKind protocol
|
||||||
|
defp extract_struct_error_message(error) do
|
||||||
|
try do
|
||||||
|
Ash.ErrorKind.message(error)
|
||||||
|
rescue
|
||||||
|
Protocol.UndefinedError -> gettext("Failed to save member. Please try again.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue