diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/card_data/incidence/incidence_rate/incidence_rate_control_diagram.ex b/lib/health_board_web/live/live_views/dashboard_live/data/card_data/incidence/incidence_rate/incidence_rate_control_diagram.ex index 7319661..f828829 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/card_data/incidence/incidence_rate/incidence_rate_control_diagram.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/card_data/incidence/incidence_rate/incidence_rate_control_diagram.ex @@ -1,6 +1,169 @@ defmodule HealthBoardWeb.DashboardLive.CardData.IncidenceRateControlDiagram do + alias HealthBoard.Contexts + @spec fetch(map()) :: map() - def fetch(card_data) do + def fetch(%{filters: filters} = card_data) do + send(card_data.root_pid, {:exec_and_emit, &do_fetch/1, card_data, {:chart, :combo}}) + card_data + |> Map.put(:view_data, %{event_pushed: true}) + |> Map.put(:filters, Map.update!(filters, :morbidity_context, &Contexts.morbidity_name(&1))) + end + + defp do_fetch(%{id: id, data: data, filters: filters}) do + %{morbidity_context: context, from_year: from_year, to_year: to_year} = filters + %{data_periods: data_periods, weekly_morbidities: weekly_cases, yearly_populations: yearly_populations} = data + + weeks = Enum.to_list(1..53) + + data_period = fetch_data_period(data_periods, context, from_year) + + weekly_cases = Map.get(weekly_cases, context, []) + rates = fetch_rates(weekly_cases, yearly_populations) + + {lower, middle, upper} = moving_average_boundaries(rates, weeks, data_period, to_year) + + rates = Enum.filter(rates, &(&1.year == to_year)) + + %{ + id: id, + labels: weeks, + labelString: "Semana", + datasets: [ + %{ + type: "line", + label: "Coeficiente de incidência na semana", + backgroundColor: "#445", + borderColor: "#445", + pointRadius: 2, + borderWidth: 3, + fill: false, + data: Enum.map(weeks, &find_rate_from_week(rates, &1)) + }, + %{ + type: "line", + label: "Limite inferior do canal endêmico", + backgroundColor: "#6dac6d", + borderColor: "#6dac6d", + borderWidth: 2, + pointRadius: 1, + fill: false, + data: lower + }, + %{ + type: "line", + label: "Limite superior do canal endêmico", + backgroundColor: "#e47f7f", + borderColor: "#e47f7f", + borderWidth: 2, + pointRadius: 1, + fill: false, + data: upper + }, + %{ + type: "line", + label: "Índice endêmico", + backgroundColor: "#dbcb37", + borderColor: "#dbcb37", + borderWidth: 1, + pointRadius: 0, + fill: false, + data: middle + } + ] + } + end + + defp fetch_data_period(data_periods, context, from_year) do + data_context = Contexts.data_context!(:morbidity) + + data_periods + |> Map.fetch!(data_context) + |> Map.get(context, [%{from_year: from_year, from_week: 1}]) + |> Enum.at(0) + end + + defp fetch_rates(weekly_cases, yearly_populations) do + population_per_year = Enum.group_by(yearly_populations, & &1.year, & &1.total) + Enum.map(weekly_cases, &fetch_rate(&1, population_per_year)) + end + + defp fetch_rate(%{year: year, week: week, total: cases}, population_per_year) do + [population] = Map.get(population_per_year, year, [0]) + + if population > 0 and cases > 0 do + %{year: year, week: week, cases: cases, rate: cases * 100_000 / population} + else + %{year: year, week: week, cases: cases, rate: 0.0} + end + end + + defp moving_average_boundaries(rates, weeks, data_period, to_year) do + rates + |> weekly_rates(weeks, data_period, to_year) + |> displace_by(7) + |> Enum.zip() + |> Enum.map(&calculate_moving_average/1) + |> Enum.group_by(& &1.week, & &1.average) + |> Enum.map(&calculate_moving_average_quartile/1) + |> Enum.reverse() + |> Enum.reduce({[], [], []}, &ungroup_boundaries/2) + end + + defp weekly_rates(rates, weeks, %{from_year: from_year, from_week: from_week}, to_year) do + for year <- from_year..to_year, week <- weeks do + if year < from_year or (year == from_year and week < from_week) do + nil + else + %{week: week, rate: rate} = Enum.find(rates, %{week: week, rate: 0.0}, &(&1.year == year and &1.week == week)) + %{week: week, rate: rate} + end + end + |> Enum.reject(&is_nil/1) + end + + defp displace_by(list, amount, displaced_lists \\ nil) do + if amount == 0 do + displaced_lists + else + if displaced_lists == nil do + displace_by(list, amount - 1, [list]) + else + displace_by(list, amount - 1, [[nil | list] | displaced_lists]) + end + end + end + + defp calculate_moving_average(displaced_data) do + displaced_data + |> Tuple.to_list() + |> Enum.reverse() + |> Enum.reject(&is_nil/1) + |> calculate_average() + end + + defp calculate_average([%{week: week} | _tail] = data) do + size = length(data) + %{week: week, average: Enum.reduce(data, 0.0, &(&1.rate + &2)) / size} + end + + defp calculate_moving_average_quartile({_week, moving_averages}) do + { + Statistics.percentile(moving_averages, 25), + Statistics.percentile(moving_averages, 50), + Statistics.percentile(moving_averages, 75) + } + end + + defp ungroup_boundaries({q1, q2, q3}, {l1, l2, l3}) do + { + [q1 | l1], + [q2 | l2], + [q3 | l3] + } + end + + defp find_rate_from_week(rates, week) do + Enum.find_value(rates, 0.0, &if(&1.week == week, do: &1.rate, else: nil)) end end diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data.ex b/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data.ex index 63221a0..8546566 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data.ex @@ -7,8 +7,10 @@ defmodule HealthBoardWeb.DashboardLive.DashboardData do @spec assign(map) :: map def assign(%{dashboard: dashboard, data: data, filters: filters, root_pid: root_pid}) do dashboard.sections - |> Task.async_stream(&fetch_section_data(&1, data, filters, root_pid), timeout: 10_000) + |> Task.async_stream(&fetch_section_data(&1, data, filters, root_pid), timeout: 60_000) |> Enum.reduce(%{}, fn {:ok, {k, v}}, map -> Map.put(map, k, v) end) + rescue + _error -> [] end defp fetch_section_data(dashboard_section, data, filters, root_pid) do diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data/analytic.ex b/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data/analytic.ex index 47ec2bb..6e282f5 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data/analytic.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/dashboard_data/analytic.ex @@ -117,7 +117,7 @@ defmodule HealthBoardWeb.DashboardLive.DashboardData.Analytic do order_by: [asc: :context, asc: :year, asc: :week] ] |> WeeklyDeaths.list_by() - |> Enum.map(&Map.take(&1, [:context, :location_id, :year, :total])) + |> Enum.map(&Map.take(&1, [:context, :location_id, :year, :week, :total])) |> Enum.group_by(& &1.context) end @@ -131,7 +131,7 @@ defmodule HealthBoardWeb.DashboardLive.DashboardData.Analytic do order_by: [asc: :context, asc: :year, asc: :week] ] |> WeeklyMorbidities.list_by() - |> Enum.map(&Map.take(&1, [:context, :location_id, :year, :total])) + |> Enum.map(&Map.take(&1, [:context, :location_id, :year, :week, :total])) |> Enum.group_by(& &1.context) end diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/event_data.ex b/lib/health_board_web/live/live_views/dashboard_live/data/event_data.ex index 50e761e..29b0331 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/event_data.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/event_data.ex @@ -11,7 +11,7 @@ defmodule HealthBoardWeb.DashboardLive.EventData do defp build_chart_data(data, sub_type) do case sub_type do - :combo -> %{} + :combo -> combo_chart_data(data) :horizontal_bar -> %{} :line -> %{} :multiline -> multiline_chart_data(data) @@ -20,6 +20,32 @@ defmodule HealthBoardWeb.DashboardLive.EventData do end end + defp combo_chart_data(%{id: id, datasets: datasets, labels: labels, labelString: label_string}) do + %{ + id: id, + data: %{ + type: "line", + data: %{ + labels: labels, + datasets: datasets + }, + options: %{ + maintainAspectRatio: false, + scales: %{ + xAxes: [ + %{ + scaleLabel: %{ + display: true, + labelString: label_string + } + } + ] + } + } + } + } + end + defp multiline_chart_data(%{id: id, datasets: datasets, labels: labels}) do datasets = datasets diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/section_data.ex b/lib/health_board_web/live/live_views/dashboard_live/data/section_data.ex index 06cb7ca..caedee8 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/section_data.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/section_data.ex @@ -18,8 +18,10 @@ defmodule HealthBoardWeb.DashboardLive.SectionData do @spec assign(map) :: map def assign(%{section: %{cards: section_cards}, data: data, filters: filters, root_pid: root_pid}) do section_cards - |> Task.async_stream(&fetch_card_data(&1, data, filters, root_pid), timeout: 10_000) + |> Task.async_stream(&fetch_card_data(&1, data, filters, root_pid), timeout: 60_000) |> Enum.reduce(%{}, fn {:ok, {k, v}}, map -> Map.put(map, k, v) end) + rescue + _error -> [] end defp fetch_card_data(section_card, data, filters, root_pid) do diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/immediate/immediate_compulsory_analytic_control_diagrams.ex b/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/immediate/immediate_compulsory_analytic_control_diagrams.ex index f296b1f..4e82b12 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/immediate/immediate_compulsory_analytic_control_diagrams.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/immediate/immediate_compulsory_analytic_control_diagrams.ex @@ -1,6 +1,6 @@ defmodule HealthBoardWeb.DashboardLive.SectionData.ImmediateCompulsoryAnalyticControlDiagrams do - @data_keys ~w[data_periods weekly_deaths weekly_morbidities weekly_populations]a - @filter_keys ~w[morbidity_context]a + @data_keys ~w[data_periods weekly_deaths weekly_morbidities yearly_populations]a + @filter_keys ~w[from_year morbidity_context to_year]a @spec fetch(map()) :: map() def fetch(%{data: data, filters: filters} = section_data) do diff --git a/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/weekly/weekly_compulsory_analytic_control_diagrams.ex b/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/weekly/weekly_compulsory_analytic_control_diagrams.ex index 16c4aef..5b42a58 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/weekly/weekly_compulsory_analytic_control_diagrams.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/data/section_data/analytic/weekly/weekly_compulsory_analytic_control_diagrams.ex @@ -1,6 +1,6 @@ defmodule HealthBoardWeb.DashboardLive.SectionData.WeeklyCompulsoryAnalyticControlDiagrams do - @data_keys ~w[data_periods weekly_deaths weekly_morbidities weekly_populations]a - @filter_keys ~w[morbidity_context]a + @data_keys ~w[data_periods weekly_deaths weekly_morbidities yearly_populations]a + @filter_keys ~w[from_year morbidity_context to_year]a @spec fetch(map()) :: map() def fetch(%{data: data, filters: filters} = section_data) do diff --git a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard.ex b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard.ex index 90565ec..a8fa19f 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard.ex @@ -1,7 +1,7 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard do use Surface.Component - alias HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard.{History, Region, Summary} + alias HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard.{ControlDiagrams, History, Region, Summary} alias HealthBoardWeb.LiveComponents.{Card, Grid, Section, SectionHeader} alias Phoenix.LiveView @@ -64,6 +64,128 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard do :weekly_compulsory_death_rate_table ]}} /> + + + + """ else @@ -86,126 +208,4 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard do """ end end - - # - - # end diff --git a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagram.ex b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagram.ex index e3da8ca..a12bc66 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagram.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagram.ex @@ -1,20 +1,26 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard.ControlDiagram do use Surface.Component - alias HealthBoardWeb.LiveComponents.{Card, CardBodyCanvas} + alias HealthBoardWeb.LiveComponents.{Card, CardBodyCanvas, CardHeaderMenu, CardOffcanvasMenu} alias Phoenix.LiveView - prop id, :atom, required: true + prop card_id, :atom, required: true prop card, :map, required: true @spec render(map()) :: LiveView.Rendered.t() def render(assigns) do ~H""" - + + + + + """ end diff --git a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagrams.ex b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagrams.ex index bb64183..139d043 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagrams.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/control_diagrams.ex @@ -20,8 +20,7 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard.ControlDiagra diff --git a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/history_chart.ex b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/history_chart.ex index 6f5072d..3c04162 100644 --- a/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/history_chart.ex +++ b/lib/health_board_web/live/live_views/dashboard_live/fragments/analytic_dashboard/history_chart.ex @@ -11,7 +11,7 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard.HistoryChart @spec render(map()) :: LiveView.Rendered.t() def render(assigns) do ~H""" - + @@ -20,7 +20,7 @@ defmodule HealthBoardWeb.DashboardLive.Fragments.AnalyticDashboard.HistoryChart - + """ end