ammocpr/lib/ammoprices/scraping/scrape_job.ex
Graham McIntire bbe5bde145
Initial implementation of ammo price tracker
Phoenix 1.8 app that scrapes ammunition retailers (Lucky Gunner,
SGAmmo) for price data and displays historical price trends.

- Data model: retailers, calibers, products, price snapshots
- Scraper infrastructure with Req, Floki, realistic browser headers
- Oban-scheduled scrape jobs (every 4h with randomized delays)
- LiveView pages: homepage with category cards, caliber detail with
  price table, Chart.js price history, and price stats banner
- 18 seeded calibers across handgun/rifle/rimfire/shotgun categories
- 77 tests
2026-03-11 15:58:12 -05:00

36 lines
868 B
Elixir

defmodule Ammoprices.Scraping.ScrapeJob do
@moduledoc false
use Oban.Worker, queue: :scraping, max_attempts: 3
alias Ammoprices.Catalog
alias Ammoprices.Scraping.Runner
@scrapers [
Ammoprices.Scraping.Retailers.LuckyGunner,
Ammoprices.Scraping.Retailers.SgAmmo
]
@impl Oban.Worker
def perform(_job) do
calibers = Catalog.list_calibers()
for scraper <- @scrapers, caliber <- calibers do
scrape_delay()
Runner.run(scraper, caliber)
end
Phoenix.PubSub.broadcast(Ammoprices.PubSub, "prices:updated", {:prices_updated, %{}})
:ok
end
# Random delay between requests: 5-15 seconds in prod, 0 in test
defp scrape_delay do
{min, max} = Application.get_env(:ammoprices, :scrape_delay_ms, {5_000, 15_000})
delay = Enum.random(min..max)
if delay > 0 do
Process.sleep(delay)
end
end
end