diff options
Diffstat (limited to 'lib/lsg_irc/coronavirus_plugin.ex')
-rw-r--r-- | lib/lsg_irc/coronavirus_plugin.ex | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/lib/lsg_irc/coronavirus_plugin.ex b/lib/lsg_irc/coronavirus_plugin.ex new file mode 100644 index 0000000..9a017f3 --- /dev/null +++ b/lib/lsg_irc/coronavirus_plugin.ex @@ -0,0 +1,131 @@ +defmodule LSG.IRC.CoronavirusPlugin do + require Logger + @moduledoc """ + # Corona Virus + + Données de [Johns Hopkins University](https://github.com/CSSEGISandData/COVID-19) et mises à jour a peu près tous les jours. + + * `!coronavirus [France | Country]`: :-) + * `!coronavirus`: top 10 confirmés et non guéris + * `!coronavirus confirmés`: top 10 confirmés + * `!coronavirus morts`: top 10 morts + * `!coronavirus soignés`: top 10 soignés + """ + def irc_doc, do: @moduledoc + + def start_link(), do: GenServer.start_link(__MODULE__, []) + + def init(_) do + {:ok, _} = Registry.register(IRC.PubSub, "trigger:coronavirus", []) + {data, next} = fetch_data(%{}) + :timer.send_after(next, :update) + {:ok, %{data: data}} + end + + def handle_info(:update, state) do + {data, next} = fetch_data(state.data) + :timer.send_after(next, :update) + {:noreply, %{data: data}} + end + + def handle_info({:irc, :trigger, "coronavirus", m = %IRC.Message{trigger: %{type: :bang, args: args}}}, state) when args in [[], ["morts"], ["confirmés"], ["soignés"], ["malades"]] do + {field, name} = case args do + ["confirmés"] -> {:confirmed, "confirmés"} + ["morts"] -> {:deaths, "morts"} + ["soignés"] -> {:recovered, "soignés"} + _ -> {:current, "malades"} + end + sorted = state.data + |> Enum.filter(fn({_, %{region: region}}) -> region == true end) + |> Enum.map(fn({location, data}) -> {location, Map.get(data, field, 0)} end) + |> Enum.sort_by(fn({_,count}) -> count end, &>=/2) + |> Enum.take(10) + |> Enum.with_index() + |> Enum.map(fn({{location, count}, index}) -> + "##{index+1}: #{location} #{count}" + end) + |> Enum.intersperse(" - ") + |> Enum.join() + m.replyfun.("Corona virus top 10 #{name}: " <> sorted) + {:noreply, state} + end + + def handle_info({:irc, :trigger, "coronavirus", m = %IRC.Message{trigger: %{type: :bang, args: location}}}, state) do + location = Enum.join(location, " ") |> String.downcase() + if data = Map.get(state.data, location) do + m.replyfun.("Corona virus: #{location}: #{data.current} malades, #{data.confirmed} confirmés, #{data.deaths} morts, #{data.recovered} soignés") + end + {:noreply, state} + end + + def handle_info({:irc, :trigger, "coronavirus", m = %IRC.Message{trigger: %{type: :query, args: location}}}, state) do + m.replyfun.("https://github.com/CSSEGISandData/COVID-19") + {:noreply, state} + end + + # 1. Try to fetch data for today + # 2. Fetch yesterday if no results + defp fetch_data(current_data, date \\ nil) do + now = Date.utc_today() + url = fn(date) -> + "https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_daily_reports/#{date}.csv" + end + request_date = date || now + Logger.debug("Coronavirus check date: #{inspect request_date}") + {:ok, date_s} = Timex.format({request_date.year, request_date.month, request_date.day}, "%m-%d-%Y", :strftime) + cur_url = url.(date_s) + Logger.debug "Fetching URL #{cur_url}" + case HTTPoison.get(cur_url, [], follow_redirect: true) do + {:ok, %HTTPoison.Response{status_code: 200, body: csv}} -> + # Parse CSV update data + data = csv + |> String.strip() + |> String.split("\n") + |> Enum.drop(1) + |> Enum.reduce(%{}, fn(line, acc) -> + [state, region, update, confirmed, deaths, recovered,_lat, _lng] = line + |> String.strip() + |> String.split(",") + + state = String.downcase(state) + region = String.downcase(region) + confirmed = String.to_integer(confirmed) + deaths = String.to_integer(deaths) + recovered = String.to_integer(recovered) + + current = (confirmed - recovered) - deaths + + entry = %{update: update, confirmed: confirmed, deaths: deaths, recovered: recovered, current: current, region: region} + + acc = if state && state != "" do + Map.put(acc, state, entry) + else + acc + end + + region_entry = Map.get(acc, region, %{update: nil, confirmed: 0, deaths: 0, recovered: 0, current: 0}) + region_entry = %{ + update: region_entry.update || update, + confirmed: region_entry.confirmed + confirmed, + deaths: region_entry.deaths + deaths, + current: region_entry.current + current, + recovered: region_entry.recovered + recovered, + region: true + } + + Map.put(acc, region, region_entry) + end) + Logger.info "Updated coronavirus database" + {data, :timer.minutes(60)} + {:ok, %HTTPoison.Response{status_code: 404}} -> + Logger.debug "Corona 404 #{cur_url}" + date = Date.add(date || now, -1) + fetch_data(current_data, date) + other -> + Logger.error "Coronavirus: Update failed #{inspect other}" + {current_data, :timer.minutes(5)} + end + end + +end + |