Skip to content

Commit

Permalink
Create morbidity & demographic dashboards
Browse files Browse the repository at this point in the history
  • Loading branch information
altjohndev committed Dec 16, 2020
1 parent ee078b6 commit 69cdf94
Show file tree
Hide file tree
Showing 91 changed files with 2,841 additions and 485 deletions.
5 changes: 4 additions & 1 deletion assets/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ $blue-4: #5675ca;
}

.hb-map {
height: 100%;
max-height: calc(100% - 150px);
min-height: 500px;
overflow: hidden;
}

.hb-no-padding {
Expand Down Expand Up @@ -185,7 +188,7 @@ i.hb-right {
}

.hb-card-menu {
height: 50px;
height: 33px;
}

.hb-description-list > dt {
Expand Down
7 changes: 5 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import NProgress from "nprogress"
import { LiveSocket } from "phoenix_live_view"

import { renderMap } from "./map_render"
import { renderChart } from "./chart_render"

window.UIkit = UIkit
window.UIkit.use(Icons)
Expand All @@ -23,16 +24,18 @@ window.MathJax = { MathML: { extensions: ["mml3.js", "content-mathml.js"] } }
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")

let Hooks = {}
let maps = {}
let charts = {}

Hooks.Chart = {
mounted() {
this.handleEvent("chart_data", ({ id, data }) => new ChartJS(id, data))
this.handleEvent("chart_data", (data) => renderChart(ChartJS, charts, data))
}
}

Hooks.Map = {
mounted() {
this.handleEvent("map_data", (data) => renderMap(L, data))
this.handleEvent("map_data", (data) => renderMap(L, maps, data))
}
}

Expand Down
22 changes: 22 additions & 0 deletions assets/js/chart_render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const build_data = (subType, data) => {
if (subType === "pyramidBar") {
data.options.scales.xAxes[0].ticks.callback = (v) => v < 0 ? -v : v
data.options.tooltips.callbacks.label = (c) => {
console.log(c)
let value = Number(c.value)
value = value < 0 ? -value : value

let datasetLabel = data.data.datasets[c.datasetIndex].label
return `${datasetLabel}: ${value}`
}
}
return data
}

export const renderChart = (ChartJS, charts, { id, subType, data }) => {
if (charts[id]) {
charts[id].destroy()
}

charts[id] = new ChartJS(id, build_data(subType, data))
}
13 changes: 11 additions & 2 deletions assets/js/map_render.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const createInfoControl = (L, map, data) => {

if (item !== null && item !== undefined) {
L.DomUtil.removeClass(info._infoDiv, "hb-hide")
info._infoDiv.innerHTML = `<div class="uk-card-header"><h3 class="uk-card-title">${item.name}</h3><span class="uk-label" style="background: ${item.color};">${item.formatted_value}</span></div>`
info._infoDiv.innerHTML = `<div class="uk-card-header"><h3 class="uk-card-title">${item.label}</h3><span class="uk-label" style="background: ${item.color};">${item.value}</span></div>`
}
} else {
L.DomUtil.addClass(info._infoDiv, "hb-hide")
Expand Down Expand Up @@ -81,11 +81,20 @@ const fetchGeoJson = (L, map, info, data, geojson) => {
return group
}

export const renderMap = (L, { id, data, geojson_path, tile_layer_url }) => {
export const renderMap = (L, maps, { id, data, geojson_path, tile_layer_url }) => {
fetch(geojson_path).then((geojson) => geojson.json()).then((geojson) => {
if (maps[id]) {
maps[id].off()
maps[id].remove()
delete (maps[id])
}

let map = L.map(id, { scrollWheelZoom: false })

let info = createInfoControl(L, map, data).addTo(map)
fetchGeoJson(L, map, info, data, geojson).addTo(map)
L.tileLayer(tile_layer_url, { id: "mapbox/light-v9", maxZoom: 18, tileSize: 512, zoomOffset: -1 }).addTo(map)

maps[id] = map
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ defmodule HealthBoard.Contexts.Demographic.YearlyBirths do

@spec new(keyword) :: schema
def new(params \\ []) do
@schema
|> struct(params)
|> @schema.add_total()
struct(@schema, params)
end

@spec get_by(keyword) :: schema
Expand Down
57 changes: 43 additions & 14 deletions lib/health_board_web/helpers/choropleth.ex
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
defmodule HealthBoardWeb.Helpers.Choropleth do
@spec group(list(float | integer), float | integer) :: String.t()
@spec group(list(float | integer), float | integer) :: integer()
def group(ranges, value) do
case Enum.find(ranges, &on_boundary?(value, &1)) do
nil -> 0
%{group: group} -> group
end
end

@spec quartile(list(float)) :: list(map)
def quartile(data) do
{q0, q1, q2, q3} =
try do
{
0.0,
Statistics.percentile(data, 25),
Statistics.percentile(data, 50),
Statistics.percentile(data, 75)
}
rescue
_error -> {0.0, 0.0, 0.0, 0.0}
end
@spec group_color(list(float | integer), float | integer) :: String.t()
def group_color(ranges, value) do
case Enum.find(ranges, &on_boundary?(value, &1)) do
nil ->
"#cccccc"

%{group: group} ->
case group do
0 -> "#cccccc"
1 -> "#6dac6d"
2 -> "#c5cc69"
3 -> "#dbcb37"
4 -> "#e47f7f"
end
end
end

@spec quartile(list(float), keyword) :: list(map)
def quartile(data, opts \\ []) do
{q0, q1, q2, q3} = calculate_quartile(data, Keyword.get(opts, :type, :float))

[
%{from: nil, to: q0, group: 0},
Expand All @@ -34,6 +41,28 @@ defmodule HealthBoardWeb.Helpers.Choropleth do
)
end

defp calculate_quartile(data, :float) do
{
0.0,
Statistics.percentile(data, 25),
Statistics.percentile(data, 50),
Statistics.percentile(data, 75)
}
rescue
_error -> {0.0, 0.0, 0.0, 0.0}
end

defp calculate_quartile(data, :integer) do
{
0,
round(Statistics.percentile(data, 25)),
round(Statistics.percentile(data, 50)),
round(Statistics.percentile(data, 75))
}
rescue
_error -> {0, 0, 0, 0}
end

defp on_boundary?(value, boundary) do
case boundary do
%{from: nil, to: to} -> value <= to
Expand Down
6 changes: 6 additions & 0 deletions lib/health_board_web/helpers/colors.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ defmodule HealthBoardWeb.Helpers.Colors do
@divergent_colors ~w[#00aaaa #3d914b #7c6800 #aa0000 #b00038 #920072 #4655aa #b2438a #d6544c #bb8811]
@divergent_colors_amount Enum.count(@divergent_colors)

@spec blue_with_border :: {String.t(), String.t()}
def blue_with_border, do: {"rgba(54, 162, 235, 0.2)", "#36a2eb"}

@spec red_with_border :: {String.t(), String.t()}
def red_with_border, do: {"rgba(235, 162, 54, 0.2)", "#eba236"}

@spec divergents(non_neg_integer, list(String.t())) :: list(String.t())
def divergents(amount, current_colors \\ []) do
if amount > @divergent_colors_amount do
Expand Down
6 changes: 6 additions & 0 deletions lib/health_board_web/helpers/humanize.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ defmodule HealthBoardWeb.Helpers.Humanize do
above_average: "Acima da média",
average: "Média",
below_average: "Abaixo da média",
births: "Nascidos vivos",
deaths: "Óbitos",
extraction_date: "Data de extração",
female: "Feminino",
first_record_date: "Data do primeiro registro",
from_year: "Início do período anual",
last_record_date: "Data do último registro",
location: "Localidade",
locations: "Localidades",
male: "Masculino",
morbidity_context: "Doença, agravo ou evento de saúde pública de notificação compulsória",
morbidity_contexts: "Doenças, agravos e eventos de saúde pública de notificação compulsória",
morbidity: "Casos",
on_average: "Na média",
overall_severity: "Situação geral",
population: "População residente",
rate: "Taxa",
ratio: "Razão",
severity: "Situação",
to_year: "Término do período anual",
year: "Ano"
Expand Down
4 changes: 2 additions & 2 deletions lib/health_board_web/live/components/card/card_header_menu.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ defmodule HealthBoardWeb.LiveComponents.CardHeaderMenu do
~H"""
<div class={{ "uk-card-header", "uk-text-middle", "uk-visible-toggle", "show-when-not-hover-container", "uk-transition-toggle", "hb-border": border_color, "hb-border-bottom": border_color, "hb-border-#{border_color}": border_color }}>
<h3 class={{"uk-card-title", "show-when-not-hover", "uk-margin-remove-bottom"}}>
{{ @card.name }}
{{ @card.name || @card.card.name }}
</h3>
<div class={{ "uk-hidden-hover", "uk-transition-slide-top", "uk-flex", "uk-flex-middle", "uk-flex-between", "hb-card-menu"}}>
<div :if={{ @show_link and not is_nil(@card.link) and Enum.any?(@data) }}>
<a href={{ dashboard_path(@socket, "analytic", @data[:params] || []) }} uk-tooltip="Ver painel" target="_blank">
<a href={{ dashboard_path(@socket, @card.link, @data[:params] || []) }} uk-tooltip="Ver painel" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" width="24px" height="24px"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/></svg>
</a>
</div>
Expand Down
20 changes: 12 additions & 8 deletions lib/health_board_web/live/components/card/card_offcanvas_menu.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule HealthBoardWeb.LiveComponents.CardOffcanvasMenu do
<button class="uk-modal-close" type="button" uk-close></button>
<h3>
Informações sobre {{ @card.name }}
Informações sobre {{ @card.name || @card.card.name }}
</h3>
<dl class={{"uk-description-list", "hb-description-list"}}>
Expand All @@ -47,7 +47,7 @@ defmodule HealthBoardWeb.LiveComponents.CardOffcanvasMenu do
<button class="uk-modal-close" type="button" uk-close></button>
<h3>
Dados de {{ @card.name }}
Dados de {{ @card.name || @card.card.name }}
</h3>
<CardOffcanvasDescription data={{ @data[:result] || %{} }} />
Expand All @@ -59,7 +59,7 @@ defmodule HealthBoardWeb.LiveComponents.CardOffcanvasMenu do
<button class="uk-modal-close" type="button" uk-close></button>
<h3>
Legenda para {{ @card.name }}
Legenda para {{ @card.name || @card.card.name }}
</h3>
<div :if={{ Map.has_key?(@data, :labels) }}>
Expand All @@ -78,7 +78,7 @@ defmodule HealthBoardWeb.LiveComponents.CardOffcanvasMenu do
<button class="uk-modal-close" type="button" uk-close></button>
<h3>
Filtros de {{ @card.name }}
Filtros de {{ @card.name || @card.card.name }}
</h3>
<CardOffcanvasDescription data={{ @data[:filters] || %{} }} />
Expand All @@ -90,7 +90,7 @@ defmodule HealthBoardWeb.LiveComponents.CardOffcanvasMenu do
<button class="uk-modal-close" type="button" uk-close></button>
<h3>
Fontes de {{ @card.name }}
Fontes de {{ @card.name || @card.card.name }}
</h3>
<div :for={{ indicator_source <- @card.card.indicator.sources }}>
Expand All @@ -113,7 +113,11 @@ defmodule HealthBoardWeb.LiveComponents.CardOffcanvasMenu do
"""
end

defp label_description(nil, to, suffix), do: "#{to}#{suffix}"
defp label_description(from, nil, suffix), do: "#{from}#{suffix} ou mais"
defp label_description(from, to, suffix), do: "Entre #{from}#{suffix} e #{to}#{suffix}"
defp label_description(from, to, suffix) do
case {from, to} do
{nil, to} -> "#{Humanize.number(to)}#{suffix}"
{from, nil} -> "#{Humanize.number(from)}#{suffix} ou mais"
_ -> "Entre #{Humanize.number(from)}#{suffix} e #{Humanize.number(to)}#{suffix}"
end
end
end
13 changes: 12 additions & 1 deletion lib/health_board_web/live/components/dashboard.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule HealthBoardWeb.LiveComponents.Dashboard do
use Surface.LiveComponent

alias HealthBoard.Contexts
alias HealthBoardWeb.LiveComponents.{DashboardMenu, Filters, Section, SectionHeader, Tabs}
alias Phoenix.LiveView

Expand All @@ -17,7 +18,7 @@ defmodule HealthBoardWeb.LiveComponents.Dashboard do
<Filters id="filters" filters={{ @filters }} options={{ @filters_options }} />
<SectionHeader title={{ @dashboard.name }} description={{ @dashboard.description }} />
<SectionHeader title={{ title(@dashboard.name, @filters) }} description={{ @dashboard.description }} />
<Tabs
:if={{ Enum.count(@dashboard.groups) > 1 }}
Expand All @@ -31,4 +32,14 @@ defmodule HealthBoardWeb.LiveComponents.Dashboard do
</Section>
"""
end

defp title(name, filters) do
if Map.has_key?(filters, :morbidity_context) and filters[:id] == "morbidity" do
"Painel de #{Contexts.morbidity_name(filters.morbidity_context)}"
else
name
end
rescue
_error -> name
end
end
10 changes: 9 additions & 1 deletion lib/health_board_web/live/components/dashboard_menu.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule HealthBoardWeb.LiveComponents.DashboardMenu do
<ul class="uk-nav-sub">
<li :for={{ card <- section.cards }}>
<a href="">
<a href={{ "##{card.id}" }}>
{{ card.name || card.card.name }}
</a>
</li>
Expand All @@ -34,6 +34,14 @@ defmodule HealthBoardWeb.LiveComponents.DashboardMenu do
</ul>
</li>
</ul>
<hr>
<ul class="uk-nav uk-nav-default">
<li class="uk-nav-header">Outros Painéis</li>
<li :if={{ @dashboard.id != "analytic" }}><a href="/analytic">Painel de situação</a></li>
<li :if={{ @dashboard.id != "demographic" }}><a href="/demographic">Painel demográfico</a></li>
</ul>
</div>
</div>
"""
Expand Down
2 changes: 2 additions & 0 deletions lib/health_board_web/live/components/data_card.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ defmodule HealthBoardWeb.LiveComponents.DataCard do
"hb-border": border_color,
"hb-border-#{border_color}": border_color
}}>
<a name={{ @id }}></a>
<slot
name="header"
:props={{ data: @data, border_color: border_color, title: @title}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
defmodule HealthBoardWeb.DashboardLive.CardData.Births do
@spec fetch(pid, map, map) :: nil
def fetch(_pid, _card, _data) do
nil
@spec fetch(pid, map, map) :: map
def fetch(_pid, _card, data) do
%{year_births: %{total: births}} = data

%{
filters: %{
year: data.year,
location: data.location_name
},
result: %{births: births}
}
end
end
Loading

0 comments on commit 69cdf94

Please sign in to comment.