formatting and refactoring

This commit is contained in:
carla 2026-01-23 17:52:09 +01:00 committed by Moritz
parent bf7e47ce5c
commit 0fe4a55e80
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 93 additions and 104 deletions

View file

@ -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,

View file

@ -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