formatting and refactoring
This commit is contained in:
parent
bf7e47ce5c
commit
0fe4a55e80
2 changed files with 93 additions and 104 deletions
|
|
@ -364,73 +364,41 @@ 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
|
|
||||||
{:noreply,
|
|
||||||
put_flash(
|
|
||||||
socket,
|
|
||||||
:error,
|
|
||||||
gettext("Import is already running. Please wait for it to complete.")
|
|
||||||
)}
|
|
||||||
else
|
|
||||||
# Check if upload is completed
|
|
||||||
upload_entries = socket.assigns.uploads.csv_file.entries
|
|
||||||
|
|
||||||
if Enum.empty?(upload_entries) do
|
:ok ->
|
||||||
{:noreply,
|
process_csv_upload(socket)
|
||||||
put_flash(
|
end
|
||||||
socket,
|
|
||||||
:error,
|
|
||||||
gettext("Please select a CSV file to import.")
|
|
||||||
)}
|
|
||||||
else
|
|
||||||
entry = List.first(upload_entries)
|
|
||||||
|
|
||||||
if entry.done? do
|
|
||||||
with {:ok, content} <- consume_and_read_csv(socket) do
|
|
||||||
case MemberCSV.prepare(content) do
|
|
||||||
{:ok, import_state} ->
|
|
||||||
total_chunks = length(import_state.chunks)
|
|
||||||
|
|
||||||
progress = %{
|
|
||||||
inserted: 0,
|
|
||||||
failed: 0,
|
|
||||||
errors: [],
|
|
||||||
warnings: import_state.warnings || [],
|
|
||||||
status: :running,
|
|
||||||
current_chunk: 0,
|
|
||||||
total_chunks: total_chunks,
|
|
||||||
errors_truncated?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
socket =
|
|
||||||
socket
|
|
||||||
|> assign(:import_state, import_state)
|
|
||||||
|> assign(:import_progress, progress)
|
|
||||||
|> assign(:import_status, :running)
|
|
||||||
|
|
||||||
send(self(), {:process_chunk, 0})
|
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
|
|
||||||
{:error, error} ->
|
|
||||||
error_message =
|
|
||||||
case error do
|
|
||||||
%{message: msg} when is_binary(msg) -> msg
|
|
||||||
%{errors: errors} when is_list(errors) -> inspect(errors)
|
|
||||||
reason when is_binary(reason) -> reason
|
|
||||||
other -> inspect(other)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply,
|
# Checks if import can be started (admin permission, status, upload ready)
|
||||||
put_flash(
|
defp check_import_prerequisites(socket) do
|
||||||
socket,
|
cond do
|
||||||
:error,
|
not Authorization.can?(socket.assigns[:current_user], :create, Mv.Membership.Member) ->
|
||||||
gettext("Failed to prepare CSV import: %{error}", error: error_message)
|
{: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
|
||||||
|
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
|
else
|
||||||
{:error, reason} when is_binary(reason) ->
|
{:error, reason} when is_binary(reason) ->
|
||||||
{:noreply,
|
{:noreply,
|
||||||
|
|
@ -441,13 +409,7 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
error_message =
|
error_message = format_error_message(error)
|
||||||
case error do
|
|
||||||
%{message: msg} when is_binary(msg) -> msg
|
|
||||||
%{errors: errors} when is_list(errors) -> inspect(errors)
|
|
||||||
other -> inspect(other)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
put_flash(
|
put_flash(
|
||||||
socket,
|
socket,
|
||||||
|
|
@ -455,23 +417,44 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
gettext("Failed to prepare CSV import: %{error}", error: error_message)
|
gettext("Failed to prepare CSV import: %{error}", error: error_message)
|
||||||
)}
|
)}
|
||||||
end
|
end
|
||||||
else
|
|
||||||
{:noreply,
|
|
||||||
put_flash(
|
|
||||||
socket,
|
|
||||||
:error,
|
|
||||||
gettext("Please wait for the file upload to complete before starting the import.")
|
|
||||||
)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Starts the import process
|
||||||
|
defp start_import(socket, import_state) do
|
||||||
|
progress = initialize_import_progress(import_state)
|
||||||
|
|
||||||
|
socket =
|
||||||
|
socket
|
||||||
|
|> assign(:import_state, import_state)
|
||||||
|
|> assign(:import_progress, progress)
|
||||||
|
|> assign(:import_status, :running)
|
||||||
|
|
||||||
|
send(self(), {:process_chunk, 0})
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# 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
|
end
|
||||||
else
|
|
||||||
{:noreply,
|
# Formats error messages for display
|
||||||
put_flash(
|
defp format_error_message(error) do
|
||||||
socket,
|
case error do
|
||||||
:error,
|
%{message: msg} when is_binary(msg) -> msg
|
||||||
gettext("Only administrators can import members from CSV files.")
|
%{errors: errors} when is_list(errors) -> inspect(errors)
|
||||||
)}
|
reason when is_binary(reason) -> reason
|
||||||
|
other -> inspect(other)
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -28,16 +28,20 @@ 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
|
||||||
|
check_attempt_limit(attempt, max_attempts, html)
|
||||||
|
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
|
if attempt < max_attempts do
|
||||||
# Process any pending messages
|
|
||||||
:timer.sleep(50)
|
:timer.sleep(50)
|
||||||
{:cont, nil}
|
{:cont, nil}
|
||||||
else
|
else
|
||||||
{:halt, html}
|
{:halt, html}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "Global Settings LiveView" do
|
describe "Global Settings LiveView" do
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue