Fix CSV parsing and page number input validation
- RFC 4180 CSV parser: handles quoted fields with commas and escaped
quotes ("Smith, John" no longer breaks parsing)
- Safe page number parsing: non-numeric input defaults to page 1
instead of raising ArgumentError
This commit is contained in:
parent
79af4e4959
commit
2b14e836f1
2 changed files with 48 additions and 2 deletions
|
|
@ -56,7 +56,7 @@ defmodule Microwaveprop.Radio.CsvImport do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse_and_import_row(line, submitter_email) do
|
defp parse_and_import_row(line, submitter_email) do
|
||||||
fields = line |> String.split(",") |> Enum.map(&String.trim/1)
|
fields = parse_csv_fields(line)
|
||||||
|
|
||||||
if length(fields) == @expected_columns do
|
if length(fields) == @expected_columns do
|
||||||
attrs =
|
attrs =
|
||||||
|
|
@ -174,6 +174,48 @@ defmodule Microwaveprop.Radio.CsvImport do
|
||||||
{:ok, "#{y}-#{m}-#{d}T#{h}:#{mi}:#{s}Z"}
|
{:ok, "#{y}-#{m}-#{d}T#{h}:#{mi}:#{s}Z"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# RFC 4180 CSV field parser. Handles quoted fields with commas and escaped quotes.
|
||||||
|
defp parse_csv_fields(line) do
|
||||||
|
line
|
||||||
|
|> String.trim()
|
||||||
|
|> do_parse_fields([], "")
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.map(&String.trim/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_parse_fields("", acc, current), do: [current | acc]
|
||||||
|
|
||||||
|
defp do_parse_fields(<<"\"", rest::binary>>, acc, "") do
|
||||||
|
parse_quoted_field(rest, acc, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_parse_fields(<<",", rest::binary>>, acc, current) do
|
||||||
|
do_parse_fields(rest, [current | acc], "")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_parse_fields(<<c, rest::binary>>, acc, current) do
|
||||||
|
do_parse_fields(rest, acc, current <> <<c>>)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_quoted_field(<<"\"\"", rest::binary>>, acc, current) do
|
||||||
|
parse_quoted_field(rest, acc, current <> "\"")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_quoted_field(<<"\"", rest::binary>>, acc, current) do
|
||||||
|
# End of quoted field — skip to next comma or end
|
||||||
|
case rest do
|
||||||
|
<<"," , rest2::binary>> -> do_parse_fields(rest2, [current | acc], "")
|
||||||
|
"" -> [current | acc]
|
||||||
|
_ -> do_parse_fields(rest, [current | acc], "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_quoted_field(<<c, rest::binary>>, acc, current) do
|
||||||
|
parse_quoted_field(rest, acc, current <> <<c>>)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_quoted_field("", acc, current), do: [current | acc]
|
||||||
|
|
||||||
defp changeset_error_strings(changeset) do
|
defp changeset_error_strings(changeset) do
|
||||||
changeset
|
changeset
|
||||||
|> Ecto.Changeset.traverse_errors(fn {message, opts} ->
|
|> Ecto.Changeset.traverse_errors(fn {message, opts} ->
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,11 @@ defmodule MicrowavepropWeb.ContactLive.Index do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_params(params, _uri, socket) do
|
def handle_params(params, _uri, socket) do
|
||||||
page = params |> Map.get("page", "1") |> String.to_integer() |> max(1)
|
page =
|
||||||
|
case Integer.parse(Map.get(params, "page", "1")) do
|
||||||
|
{n, _} -> max(n, 1)
|
||||||
|
:error -> 1
|
||||||
|
end
|
||||||
sort_by = validate_sort_field(Map.get(params, "sort_by", @default_sort_by))
|
sort_by = validate_sort_field(Map.get(params, "sort_by", @default_sort_by))
|
||||||
sort_order = validate_sort_order(Map.get(params, "sort_order", @default_sort_order))
|
sort_order = validate_sort_order(Map.get(params, "sort_order", @default_sort_order))
|
||||||
search = Map.get(params, "search", "")
|
search = Map.get(params, "search", "")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue