action-requests-demo.jamey..../priv/repo/seeds.exs
Jamey Greenwood de1b1bd484 Initial commit: Phoenix LiveView demo for interactive data tables with filtering, sorting, pagination, URL state, and progressive enhancement
Implements a fully-featured action requests table in a single LiveView module using Flop, Ecto, and SQLite. Includes:

- Fuzzy search, status/assignment filters, column sorting, 25-per-page pagination
- Real-time updates, bookmarkable URLs via `handle_params/3`
- JS-disabled fallback with GET forms (no duplicate logic)
- 1,000,000 seeded records, Tailwind + DaisyUI styling, light/dark themes
- Comprehensive README with comparisons to Django+React/Rails+React stacks
- 31 tests covering all scenarios

Tech: Phoenix 1.8+, LiveView, Flop, Ecto, SQLite, Elixir 1.15+
2025-11-16 10:24:06 +00:00

90 lines
2.8 KiB
Elixir

# Script for populating the database. You can run it as:
#
# mix run priv/repo/seeds.exs
#
# Inside the script, you can read and write to any of your
# repositories directly:
#
# ActionRequestsDemo.Repo.insert!(%ActionRequestsDemo.SomeSchema{})
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.
alias ActionRequestsDemo.Repo
alias ActionRequestsDemo.ActionRequests.ActionRequest
# Configuration
total_records = 1_000_000
# SQLite has a max parameter limit of 32766
# With 7 fields per record, max is ~4681 records per batch
# Using 4000 to be safe
batch_size = 4_000
num_batches = div(total_records, batch_size)
IO.puts("Seeding #{total_records} action requests in #{num_batches} batches of #{batch_size}...")
IO.puts("Started at: #{DateTime.utc_now()}")
statuses = ["resolved", "unresolved"]
# Helper function to generate a single record
generate_record = fn ->
patient_name = Faker.Person.name()
status = Enum.random(statuses)
assigned_user_id = if :rand.uniform() > 0.3, do: Enum.random(1..5), else: nil
days_ago = :rand.uniform(365)
inserted_at =
DateTime.utc_now()
|> DateTime.add(-days_ago * 24 * 3600, :second)
|> DateTime.truncate(:second)
delivery_scheduled_at =
if :rand.uniform() > 0.4 do
days_ahead = :rand.uniform(14)
NaiveDateTime.utc_now()
|> NaiveDateTime.add(days_ahead * 24 * 3600, :second)
|> NaiveDateTime.truncate(:second)
else
nil
end
%{
id: Ecto.UUID.generate(),
patient_name: patient_name,
status: status,
assigned_user_id: assigned_user_id,
delivery_scheduled_at: delivery_scheduled_at,
inserted_at: inserted_at,
updated_at: inserted_at
}
end
# Process batches sequentially (SQLite doesn't handle concurrent writes well)
start_time = System.monotonic_time(:millisecond)
Enum.each(1..num_batches, fn batch_num ->
# Generate batch of records
records = for _i <- 1..batch_size, do: generate_record.()
# Insert batch in a single transaction
{count, _} = Repo.insert_all(ActionRequest, records)
if rem(batch_num, 10) == 0 do
elapsed = div(System.monotonic_time(:millisecond) - start_time, 1000)
progress = batch_num * batch_size
rate = if elapsed > 0, do: div(progress, elapsed), else: 0
IO.puts("Progress: #{progress}/#{total_records} (#{div(progress * 100, total_records)}%) - #{rate} records/sec")
end
count
end)
end_time = System.monotonic_time(:millisecond)
elapsed_seconds = div(end_time - start_time, 1000)
records_per_second = div(total_records, max(elapsed_seconds, 1))
IO.puts("\n✓ Successfully seeded #{total_records} action requests!")
IO.puts("Total time: #{elapsed_seconds} seconds (#{records_per_second} records/sec)")
IO.puts("Finished at: #{DateTime.utc_now()}")