| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- defmodule Vaccins.LocationStore do
- defmodule Location do
- alias __MODULE__, as: Location
- @limit 60 * 60 * 24
- defstruct [
- :id,
- :name,
- :availability_query,
- :booking_page,
- :provider,
- availability_query_params: []
- ]
- def set_id(l = %Location{name: name}), do: %{l | id: name |> String.to_atom()}
- def build_query(l = %Location{availability_query_params: params, provider: provider})
- when params != [],
- do: %{l | availability_query: params |> provider.new}
- def query_availability(
- l = %Location{id: id, availability_query: q, booking_page: bp, provider: provider}
- ) do
- with url <- q |> provider.to_url() |> URI.to_string(),
- {:ok, result} <- url |> Vaccins.Scraper.get_json() do
- case result |> provider.analyze_result() do
- {:ok, slots} ->
- case slots
- |> Enum.map(&DateTime.truncate(&1, :second))
- |> Enum.group_by(&(&1 |> DateTime.diff(DateTime.utc_now()) |> abs < @limit)) do
- grouped = %{true: before_limit} ->
- {:ok, before_limit |> sort, grouped |> Map.get(false, []) |> sort}
- %{false: after_limit} ->
- {:ok, after_limit |> sort}
- end
- error = {:error, reason} when reason in [:no_availability] ->
- error
- end
- end
- end
- defp sort(list) when is_list(list), do: list |> Enum.sort_by(& &1, {:asc, DateTime})
- end
- defmodule LocationRaw do
- use Ecto.Schema
- import Ecto.Changeset
- @primary_key {:id, :id, autogenerate: false}
- embedded_schema do
- field(:name, :string)
- field(:booking_page, :string)
- field(:raw_query, :string)
- end
- @doc false
- def changeset(location \\ %__MODULE__{}, attrs),
- do:
- location
- |> cast(attrs, [:name, :booking_page, :raw_query])
- |> validate_required([:name, :booking_page, :raw_query])
- def to_query_params(%__MODULE__{raw_query: raw}) do
- raw
- |> URI.parse()
- |> Map.get(:query)
- |> URI.decode_query()
- |> Map.new(fn {k, v} -> {k |> String.to_atom(), v} end)
- |> Map.take([:agenda_ids, :limit, :practice_ids, :visit_motive_ids])
- end
- end
- require Ex2ms
- import Ecto.Changeset
- alias Vaccins.Queries.Doctolib
- @name Vaccins.LocationStore
- def get_locations() do
- select_all =
- Ex2ms.fun do
- x -> x
- end
- :dets.select(@name, select_all) |> Enum.map(fn {_k, v} -> v end)
- end
- def insert_all(locations) do
- locations |> Enum.each(&:dets.insert(@name, {&1.id, &1}))
- end
- def build_location(l = %Location{}), do: l |> Location.set_id() |> Location.build_query()
- def add_location(params) do
- cs = params |> LocationRaw.changeset()
- with {:ok, raw_location} <- cs |> apply_action(:insert),
- processed <-
- %Location{
- name: raw_location.name,
- booking_page: raw_location.booking_page,
- availability_query_params: raw_location |> LocationRaw.to_query_params(),
- provider: Doctolib
- }
- |> build_location,
- true <- :dets.insert_new(@name, {processed.id, processed}) do
- :ok
- else
- false -> cs |> add_error(:name, "error on insertion", []) |> apply_action(:insert)
- e = {:error, _} -> e
- end
- end
- end
|