Skip to content

Commit

Permalink
add PirateWeather data source (based on DarkSky API) (#24)
Browse files Browse the repository at this point in the history
* mv module

* add PirateWeather data source (based on DarkSky)

* switch to Jason + updates deps + fix warnings and format
  • Loading branch information
mayel authored Oct 6, 2023
1 parent 5a39feb commit f72ae03
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 18 deletions.
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Mix.Config
import Config

config :forecastr,
appid: System.get_env("FORECASTR_API_KEY"),
Expand Down
2 changes: 1 addition & 1 deletion lib/forecastr/geocoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule Forecastr.Geocoder do
end

defp parse_request(%HTTPoison.Response{body: body}) do
Poison.decode!(body)
Jason.decode!(body)
end

defp geocode_with_osm(path, params) do
Expand Down
2 changes: 1 addition & 1 deletion lib/forecastr/owm/owm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ defmodule Forecastr.OWM do

case Forecastr.OWM.HTTP.get(endpoint, [], params: params) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, Poison.decode!(body)}
{:ok, Jason.decode!(body)}

{:ok, %HTTPoison.Response{status_code: 404}} ->
{:error, :not_found}
Expand Down
161 changes: 161 additions & 0 deletions lib/forecastr/pirate_weather.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
defmodule Forecastr.PirateWeather do
@moduledoc false

alias Forecastr.Geocoder

@type when_to_forecast :: :today | :next_days
@spec weather(when_to_forecast, String.t(), map()) :: {:ok, map()} | {:error, atom()}
def weather(when_to_forecast, query, opts) do
case api_endpoint(when_to_forecast) do
nil ->
{:error, :no_api_key}

endpoint ->
params = convert_params(opts)

%{
"lat" => lat,
"lon" => lon,
"address" => %{
"city" => city,
"country" => country
}
} =
query
|> Geocoder.geocode()
|> pick_location()

with {:ok, forecast} <- fetch_weather_information(endpoint <> "/#{lat},#{lon}", params) do
{:ok,
forecast
|> add("name", city)
|> add("country", country)
|> add("when_to_forecast", Atom.to_string(when_to_forecast))}
end
end
end

@spec normalize(map()) :: {:ok, map()}
@doc """
Normalize for today's weather or for the next 3 days weather
"""
def normalize(%{
"when_to_forecast" => "today",
"name" => name,
"country" => country,
"latitude" => lat,
"longitude" => lon,
"currently" => %{"temperature" => temp = temp_max = temp_min} = weather
}) do
normalized =
weather
|> extract_main_weather()
|> add("name", name)
|> add("country", country)
|> add("coordinates", %{"lat" => lat, "lon" => lon})
|> add("temp", temp)
|> add("temp_max", temp_max)
|> add("temp_min", temp_min)

{:ok, normalized}
end

def normalize(%{
"when_to_forecast" => "next_days",
"name" => name,
"country" => country,
"latitude" => lat,
"longitude" => lon,
"hourly" => %{"data" => forecast_list}
})
when is_list(forecast_list) do
normalized =
Map.new()
|> add("name", name)
|> add("country", country)
|> add("coordinates", %{"lat" => lat, "lon" => lon})
|> add("list", forecast_list |> Enum.map(&normalize_forecast_list/1))

{:ok, normalized}
end

defp fetch_weather_information(endpoint, opts) do
case HTTPoison.get(endpoint, [], params: opts) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, Jason.decode!(body)}

{:ok, %HTTPoison.Response{status_code: 404}} ->
{:error, :not_found}

{:ok, %HTTPoison.Response{status_code: 400}} ->
{:error, :not_found}

{:ok, %HTTPoison.Response{status_code: 401}} ->
{:error, :api_key_invalid}

error = {:error, _reason} ->
error
end
end

defp extract_main_weather(weather) do
%{"summary" => main_weather_condition, "icon" => icon_name} = weather
%{"description" => main_weather_condition, "id" => icon_name}
end

defp normalize_forecast_list(%{
"summary" => main_weather_condition,
"icon" => icon,
"temperature" => temp = temp_max = temp_min,
"time" => time
}) do
date = time |> DateTime.from_unix!() |> DateTime.to_date() |> to_string()
time = time |> DateTime.from_unix!() |> DateTime.to_time() |> to_string()

Map.new()
|> add("temp", temp)
|> add("temp_max", temp_max)
|> add("temp_min", temp_min)
|> add("dt_txt", "#{date} #{time}")
|> add("dt", time)
|> add("description", main_weather_condition)
|> add("id", icon)
end

defp add(map, key, value) do
map
|> Map.put(key, value)
end

@spec api_endpoint(when_to_forecast) :: String.t()
def api_endpoint(_) do
case Application.get_env(:forecastr, :appid) do
appid when is_binary(appid) and appid != "" ->
"https://api.pirateweather.net/forecast/#{appid}"

_ ->
nil
end
end

defp convert_params(%{units: :imperial} = params), do: Map.put(params, :units, "us")
defp convert_params(%{units: _} = params), do: Map.put(params, :units, "si")
defp convert_params(%{} = params), do: params
defp convert_params(params) when is_list(params), do: Map.new(params) |> convert_params()

defp pick_location(%{"address" => %{"city" => _city}} = body), do: body

defp pick_location(%{"address" => %{"town" => town}} = body),
do: put_in(body, ["address", "city"], town)

defp pick_location(%{"address" => %{"village" => village}} = body),
do: put_in(body, ["address", "city"], village)

defp pick_location(%{"address" => %{"province" => province}} = body),
do: put_in(body, ["address", "city"], province)

defp pick_location(%{"address" => %{"suburb" => suburb}} = body),
do: put_in(body, ["address", "city"], suburb)

defp pick_location({"address", address}), do: %{"address" => address} |> pick_location()
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ defmodule Forecastr.Renderer.Giphy do
alias __MODULE__

@spec render(map(), units :: atom()) :: map()
def render(%{"description" => description} = map, units) do
Map.put(map, "giphy_pic", Enum.random(Giphy.HTTP.search(description)))
def render(%{"description" => description} = map, _units) do
Map.put(
map,
"giphy_pic",
case Giphy.HTTP.search(description) do
gifs when is_list(gifs) and gifs != [] -> Enum.random(gifs)
_ -> nil
end
)
end

def render(%{"list" => forecasts} = map, units) do
def render(%{"list" => forecasts} = map, _units) do
%{map | "list" => giphy(forecasts)}
end

Expand Down
2 changes: 1 addition & 1 deletion lib/forecastr/renderer/giphy/http.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defmodule Forecastr.Renderer.Giphy.HTTP do
giphy_url
|> HTTPoison.get!()
|> Map.get(:body)
|> Poison.decode!()
|> Jason.decode!()
|> extract_gif_urls()
end

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule Forecastr.MixProject do
defp deps do
[
{:httpoison, "~> 1.4"},
{:poison, "~> 5.0"},
{:jason, "~> 1.4"},
{:elbat, "~> 0.0.6"},
{:dialyxir, "~> 1.3.0", only: [:dev], runtime: false},
{:credo, "~> 1.5", only: [:dev, :test], runtime: false},
Expand Down
20 changes: 10 additions & 10 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
%{
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"},
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
"credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"},
"dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"},
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm", "e3be2bc3ae67781db529b80aa7e7c49904a988596e2dbff897425b48b3581161"},
"earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"},
"earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"},
"elbat": {:hex, :elbat, "0.0.6", "35270ae629058435c0f705fc5b281662597fda0c1c0479080e35205d2758c6a5", [:mix], [], "hexpm", "74bbac013afe869123833273e5f26826fad453e17c09aeabcb7d8d0a74baf868"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.30.4", "e8395c8e3c007321abb30a334f9f7c0858d80949af298302daf77553468c0c39", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "9a19f0c50ffaa02435668f5242f2b2a61d46b541ebf326884505dfd3dd7af5e4"},
"ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"},
"exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"},
"excoveralls": {:hex, :excoveralls, "0.17.0", "279f124dba347903bb654bc40745c493ae265d45040001b4899ea1edf88078c7", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "08b638d114387a888f9cb8d65f2a0021ec04c3e447b793efa7c1e734aba93004"},
"excoveralls": {:hex, :excoveralls, "0.17.1", "83fa7906ef23aa7fc8ad7ee469c357a63b1b3d55dd701ff5b9ce1f72442b2874", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "95bc6fda953e84c60f14da4a198880336205464e75383ec0f570180567985ae0"},
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"},
"exvcr": {:hex, :exvcr, "0.14.3", "e7d93b3b25919fbb653b06bc015cab99f658400eae96ed8e59758f1bb12ae167", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "38fa7d918aeda0ecc8e70346225c1299d21d4c432061c832f23a421ac2ed791d"},
"exvcr": {:hex, :exvcr, "0.14.4", "1aa5fe7d3f10b117251c158f8d28b39f7fc73d0a7628b2d0b75bf8cfb1111576", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4e600568c02ed29d46bc2e2c74927d172ba06658aa8b14705c0207363c44cc94"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
"httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"hackney": {:hex, :hackney, "1.19.1", "59de4716e985dd2b5cbd4954fa1ae187e2b610a9c4520ffcb0b1653c3d6e5559", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "8aa08234bdefc269995c63c2282cf3cd0e36febe3a6bfab11b610572fdd1cad0"},
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
Expand All @@ -26,7 +26,7 @@
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
Expand Down

0 comments on commit f72ae03

Please sign in to comment.