Skip to content

Commit

Permalink
Add bulk create backend for local authorities
Browse files Browse the repository at this point in the history
  • Loading branch information
mbklein committed Dec 5, 2023
1 parent bf56ef8 commit 3a3bb33
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
36 changes: 36 additions & 0 deletions app/lib/meadow_web/controllers/authority_records_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ defmodule MeadowWeb.AuthorityRecordsController do

plug(:authorize_user)

def bulk_create(conn, %{"records" => %Plug.Upload{path: path}}) do
results =
csv_to_maps(path)
|> AuthorityRecords.create_authority_records()
|> Enum.map(fn {status, %{id: id, label: label, hint: hint}} ->
["info:nul/#{id}", label, hint, status]
end)

file = "authority_import_#{DateTime.utc_now() |> DateTime.to_unix()}.csv"

conn =
conn
|> put_resp_content_type("text/csv")
|> put_resp_header("content-disposition", ~s[attachment; filename="#{file}"])
|> send_chunked(:ok)

[~w(id label hint status) | results]
|> CSV.dump_to_stream()
|> Stream.each(fn csv_row ->
chunk(conn, csv_row)
end)
|> Stream.run()

conn
end

defp csv_to_maps(file) do
[headers | rows] =
File.stream!(file, [], :line)
|> CSV.parse_stream(skip_headers: false)
|> Enum.to_list()

headers = Enum.map(headers, &String.to_atom/1)
Enum.map(rows, fn row -> Enum.zip(headers, row) |> Enum.into(%{}) end)
end

def export(conn, %{"file" => file} = params) do
export(conn, Path.extname(file), params)
end
Expand Down
1 change: 1 addition & 0 deletions app/lib/meadow_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ defmodule MeadowWeb.Router do
pipe_through(:api)

post("/export/:file", MeadowWeb.ExportController, :export)
post("/authority_records/bulk_create", MeadowWeb.AuthorityRecordsController, :bulk_create)
post("/authority_records/:file", MeadowWeb.AuthorityRecordsController, :export)
post("/create_shared_links/:file", MeadowWeb.SharedLinksController, :export)

Expand Down
51 changes: 51 additions & 0 deletions app/lib/nul/authority_records.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule NUL.AuthorityRecords do
The NUL.AuthorityRecords context.
"""

# alias Faker.NaiveDateTime
alias Meadow.Repo
alias NUL.Schemas.AuthorityRecord

Expand Down Expand Up @@ -90,6 +91,47 @@ defmodule NUL.AuthorityRecords do
|> Repo.insert!()
end

@doc """
Creates many AuthorityRecords at once. Returns a list of [{:created|:duplicate}, %AuthorityRecord{}]
where the record is either the newly created record or the retrieved existing record
"""
def create_authority_records(list_of_attrs) do
inserted_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)

records =
Enum.map(list_of_attrs, fn attrs ->
Map.merge(attrs, %{
id: Ecto.UUID.generate(),
inserted_at: inserted_at,
updated_at: inserted_at
})
end)

labels = Enum.map(records, & &1.label)

duplicates =
from(ar in AuthorityRecord, where: ar.label in ^labels)
|> Repo.all()
|> indexed_records("duplicate")

created =
Repo.insert_all(AuthorityRecord, records,
returning: true,
on_conflict: :nothing,
conflict_target: :label
)
|> indexed_records("created")

results = Enum.into(created ++ duplicates, %{})
Enum.map(labels, &Map.get(results, &1))
end

# def create_authority_records(list_of_attrs) do
# Repo.transaction(fn ->
# {:ok, Enum.map(list_of_attrs, &create_authority_record/1)} |> IO.inspect()
# end)
# end

@doc """
Updates an AuthorityRecord.
Expand All @@ -114,4 +156,13 @@ defmodule NUL.AuthorityRecords do
def delete_authority_record(%AuthorityRecord{} = authority_record) do
Repo.delete(authority_record)
end

defp indexed_records({_, records}, status), do: indexed_records(records, status)

defp indexed_records(records, status) do
records
|> Enum.map(fn record ->
{record.label, {status, record}}
end)
end
end

0 comments on commit 3a3bb33

Please sign in to comment.