# ┌───────────────────────────────────────────────────────────┐
# │ Exercise in the book "Programming Elixir" by Dave Thomas. │
# └───────────────────────────────────────────────────────────┘
defmodule NOAA.Observations do
@moduledoc """
Fetches weather observations for a US state/territory code.
"""
use PersistConfig
alias __MODULE__.{Log, State, Station}
@url_templates get_env(:url_templates)
@doc """
Fetches weather observations for a US state/territory `code`.
Returns either tuple `{:ok, [observation]}` or tuple `{:error, text}`.
## Examples
iex> alias NOAA.Observations
iex> {:ok, observations} = Observations.fetch("vt")
iex> Enum.all?(observations, &is_map/1) and length(observations) > 0
true
"""
@spec fetch(State.code()) ::
{:ok, [Station.observation()]} | {:error, String.t()}
def fetch(code) do
:ok = Log.info(:fetching_observations, {code, __ENV__})
case State.stations(code, @url_templates) do
{:ok, stations} ->
stations
|> Enum.map(&Task.async(Station, :observation, [&1, @url_templates]))
|> Enum.map(&Task.await/1)
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|> case do
%{error: errors} -> {:error, hd(errors)}
%{ok: observations} -> {:ok, observations}
%{} -> {:ok, []}
end
{:error, text} ->
{:error, text}
end
end
end