summaryrefslogtreecommitdiff
path: root/lib/lsg_irc/coronavirus_plugin.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lsg_irc/coronavirus_plugin.ex')
-rw-r--r--lib/lsg_irc/coronavirus_plugin.ex131
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
+