Commit 562cc152 authored by Peter van der meulen's avatar Peter van der meulen

add system for day-forecast

parent f8270410
Pipeline #144 passed with stage
in 1 minute and 28 seconds
defmodule AuroraBot.DayForecast do
@moduledoc """
Day Forecasts
This module downloads data from [WIP] and checks at what time the likelyhood
of seeing an aurora is the best and at which time bigger auroral activity
would start.
"""
alias AuroraBot.DayForecast.NOAAPlanetaryKForecast
alias __MODULE__
defstruct datetime: nil, kp: nil
@doc """
Returns an array of DayForecast objects where for each day the highest kp is
provided at a specific time between 19:00 and 00:00 UTC
"""
def get! do
NOAAPlanetaryKForecast.get!()
|> Enum.filter(&in_timezone_and_strong?(&1))
|> group_per_day()
|> groups_to_structs()
end
defp in_timezone_and_strong?(record) do
time = record.datetime |> Timex.to_datetime() |> DateTime.to_time()
time >= ~T[18:00:00] && record.kp >= 5
end
defp group_per_day(records) do
Enum.group_by(records, fn record -> Timex.to_date(record.datetime) end)
end
defp groups_to_structs(groups) do
Map.keys(groups)
|> Enum.map(fn key -> group_to_struct(groups[key]) end)
end
defp group_to_struct(records) do
selected = Enum.sort_by(records, fn record -> record.kp end, &>=/2) |> List.first()
struct(DayForecast, selected)
end
end
defmodule AuroraBot.DayForecast.NOAAPlanetaryKForecast do
@moduledoc """
NOAA Planetary forecasts
The end-point used fetches the KP-status observed and predicted by NOAA in
intervals of 3 hours.
"""
def get! do
case response() do
%HTTPoison.Response{status_code: 200, body: body} ->
parse_body(body)
%HTTPoison.Error{reason: reason} ->
raise reason
end
end
defp response do
HTTPoison.get!("https://services.swpc.noaa.gov/products/noaa-planetary-k-index-forecast.json")
end
defp parse_body(body) do
body
# Parse JSON
|> Jason.decode!()
# Drop the documentation in record 1
|> Enum.drop(1)
|> Enum.map(&parse_record(&1))
# Filter and keep only Estimations (< 24 hours and predictions > 24 hours)
|> Enum.filter(fn x -> x.state != "observed" end)
end
defp parse_record([datetime, kp, state, _]) do
%{
datetime: Timex.parse!(datetime, "%Y-%m-%d %H:%M:%S", :strftime),
kp: String.to_integer(kp),
state: state
}
end
end
defmodule DayForecastSpec do
use ESpec
alias AuroraBot.DayForecast
describe "get" do
it "returns no records when none have kp beyond 4" do
mock_noaa_request(4, 3, "2018-11-16 18:00:00", "2018-11-16 21:00:00")
expect DayForecast.get!() |> to(eq [])
end
it "returns the highest kp when multiple have kp beyond 4 on same day" do
mock_noaa_request(5, 6, "2018-11-16 18:00:00", "2018-11-16 21:00:00")
expected = %DayForecast{datetime: ~N[2018-11-16 21:00:00], kp: 6}
expect DayForecast.get!() |> to(eq [expected])
end
it "returns 2 objects when 2 days have kp beyond 4" do
mock_noaa_request(5, 6, "2018-11-16 18:00:00", "2018-11-17 21:00:00")
expected1 = %DayForecast{datetime: ~N[2018-11-16 18:00:00], kp: 5}
expected2 = %DayForecast{datetime: ~N[2018-11-17 21:00:00], kp: 6}
expect DayForecast.get!() |> to(eq [expected1, expected2])
end
end
def mock_noaa_request(kp1, kp2, time_tag1, time_tag2) do
response = %HTTPoison.Response{
status_code: 200,
body: "[
[
\"time_tag\",
\"kp\",
\"observed\",
\"noaa_scale\"
],
[
\"#{time_tag1}\",
\"#{kp1}\",
\"predicted\",
null
],
[
\"#{time_tag2}\",
\"#{kp2}\",
\"predicted\",
null
]]"
}
allow HTTPoison |> to(accept(:get!, fn "http" <> _ -> response end))
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment