formatting and refactoring
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
carla 2026-01-23 17:52:09 +01:00
parent d02f725d51
commit ffe146716b
3 changed files with 102 additions and 106 deletions

View file

@ -310,10 +310,17 @@ defmodule Mv.Membership.Import.MemberCSV do
case process_row(row_map, line_number, custom_field_lookup, actor) do case process_row(row_map, line_number, custom_field_lookup, actor) do
{:ok, _member} -> {:ok, _member} ->
update_inserted({acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?}) update_inserted(
{acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?}
)
{:error, error} -> {:error, error} ->
handle_row_error({acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?}, error, current_error_count, max_errors) handle_row_error(
{acc_inserted, acc_failed, acc_errors, acc_error_count, acc_truncated?},
error,
current_error_count,
max_errors
)
end end
end) end)

View file

@ -364,114 +364,97 @@ defmodule MvWeb.GlobalSettingsLive do
@impl true @impl true
def handle_event("start_import", _params, socket) do def handle_event("start_import", _params, socket) do
# Server-side admin check case check_import_prerequisites(socket) do
if Authorization.can?(socket.assigns[:current_user], :create, Mv.Membership.Member) do {:error, message} ->
# Prevent concurrent imports {:noreply, put_flash(socket, :error, message)}
if socket.assigns.import_status == :running do
:ok ->
process_csv_upload(socket)
end
end
# Checks if import can be started (admin permission, status, upload ready)
defp check_import_prerequisites(socket) do
cond do
not Authorization.can?(socket.assigns[:current_user], :create, Mv.Membership.Member) ->
{:error, gettext("Only administrators can import members from CSV files.")}
socket.assigns.import_status == :running ->
{:error, gettext("Import is already running. Please wait for it to complete.")}
Enum.empty?(socket.assigns.uploads.csv_file.entries) ->
{:error, gettext("Please select a CSV file to import.")}
not List.first(socket.assigns.uploads.csv_file.entries).done? ->
{:error,
gettext("Please wait for the file upload to complete before starting the import.")}
true ->
:ok
end
end
# Processes CSV upload and starts import
defp process_csv_upload(socket) do
with {:ok, content} <- consume_and_read_csv(socket),
{:ok, import_state} <- MemberCSV.prepare(content) do
start_import(socket, import_state)
else
{:error, reason} when is_binary(reason) ->
{:noreply, {:noreply,
put_flash( put_flash(
socket, socket,
:error, :error,
gettext("Import is already running. Please wait for it to complete.") gettext("Failed to prepare CSV import: %{reason}", reason: reason)
)} )}
else
# Check if upload is completed
upload_entries = socket.assigns.uploads.csv_file.entries
if Enum.empty?(upload_entries) do {:error, error} ->
{:noreply, error_message = format_error_message(error)
put_flash( {:noreply,
socket, put_flash(
:error, socket,
gettext("Please select a CSV file to import.") :error,
)} gettext("Failed to prepare CSV import: %{error}", error: error_message)
else )}
entry = List.first(upload_entries) end
end
if entry.done? do # Starts the import process
with {:ok, content} <- consume_and_read_csv(socket) do defp start_import(socket, import_state) do
case MemberCSV.prepare(content) do progress = initialize_import_progress(import_state)
{:ok, import_state} ->
total_chunks = length(import_state.chunks)
progress = %{ socket =
inserted: 0, socket
failed: 0, |> assign(:import_state, import_state)
errors: [], |> assign(:import_progress, progress)
warnings: import_state.warnings || [], |> assign(:import_status, :running)
status: :running,
current_chunk: 0,
total_chunks: total_chunks,
errors_truncated?: false
}
socket = send(self(), {:process_chunk, 0})
socket
|> assign(:import_state, import_state)
|> assign(:import_progress, progress)
|> assign(:import_status, :running)
send(self(), {:process_chunk, 0}) {:noreply, socket}
end
{:noreply, socket} # Initializes import progress structure
defp initialize_import_progress(import_state) do
%{
inserted: 0,
failed: 0,
errors: [],
warnings: import_state.warnings || [],
status: :running,
current_chunk: 0,
total_chunks: length(import_state.chunks),
errors_truncated?: false
}
end
{:error, error} -> # Formats error messages for display
error_message = defp format_error_message(error) do
case error do case error do
%{message: msg} when is_binary(msg) -> msg %{message: msg} when is_binary(msg) -> msg
%{errors: errors} when is_list(errors) -> inspect(errors) %{errors: errors} when is_list(errors) -> inspect(errors)
reason when is_binary(reason) -> reason reason when is_binary(reason) -> reason
other -> inspect(other) other -> inspect(other)
end
{:noreply,
put_flash(
socket,
:error,
gettext("Failed to prepare CSV import: %{error}", error: error_message)
)}
end
else
{:error, reason} when is_binary(reason) ->
{:noreply,
put_flash(
socket,
:error,
gettext("Failed to prepare CSV import: %{reason}", reason: reason)
)}
{:error, error} ->
error_message =
case error do
%{message: msg} when is_binary(msg) -> msg
%{errors: errors} when is_list(errors) -> inspect(errors)
other -> inspect(other)
end
{:noreply,
put_flash(
socket,
:error,
gettext("Failed to prepare CSV import: %{error}", error: error_message)
)}
end
else
{:noreply,
put_flash(
socket,
:error,
gettext("Please wait for the file upload to complete before starting the import.")
)}
end
end
end
else
{:noreply,
put_flash(
socket,
:error,
gettext("Only administrators can import members from CSV files.")
)}
end end
end end
@ -610,7 +593,9 @@ defmodule MvWeb.GlobalSettingsLive do
send(live_view_pid, {:chunk_done, idx, chunk_result}) send(live_view_pid, {:chunk_done, idx, chunk_result})
else else
# Start async task to process chunk in production # Start async task to process chunk in production
Task.Supervisor.async_nolink(Mv.TaskSupervisor, fn -> # Use start_child for fire-and-forget: no monitor, no Task messages
# We only use our own send/2 messages for communication
Task.Supervisor.start_child(Mv.TaskSupervisor, fn ->
{:ok, chunk_result} = {:ok, chunk_result} =
MemberCSV.process_chunk( MemberCSV.process_chunk(
chunk, chunk,

View file

@ -28,17 +28,21 @@ defmodule MvWeb.GlobalSettingsLiveTest do
if has_element?(view, "[data-testid='import-results-panel']") do if has_element?(view, "[data-testid='import-results-panel']") do
{:halt, html} {:halt, html}
else else
if attempt < max_attempts do check_attempt_limit(attempt, max_attempts, html)
# Process any pending messages
:timer.sleep(50)
{:cont, nil}
else
{:halt, html}
end
end end
end) end)
end end
# Checks if we should continue or halt based on attempt limit
defp check_attempt_limit(attempt, max_attempts, html) do
if attempt < max_attempts do
:timer.sleep(50)
{:cont, nil}
else
{:halt, html}
end
end
describe "Global Settings LiveView" do describe "Global Settings LiveView" do
setup %{conn: conn} do setup %{conn: conn} do
user = create_test_user(%{email: "admin@example.com"}) user = create_test_user(%{email: "admin@example.com"})