146 lines
3.4 KiB
Elixir
146 lines
3.4 KiB
Elixir
defmodule Growth.Indicators.Load do
|
|
@moduledoc """
|
|
Load local indicators csv files into ets.
|
|
|
|
For each measurement an ets table is created, so the system has the following tables:
|
|
|
|
* `:arm_circumference_for_age`
|
|
* `:bmi_for_age`
|
|
* `:head_circumference_for_age`
|
|
* `:height_for_age`
|
|
* `:subscapular_skinfold_for_age`
|
|
* `:telemetry_handler_table`
|
|
* `:triceps_skinfold_for_age`
|
|
* `:weight_for_age`
|
|
* `:weight_for_height`
|
|
|
|
The rows in the csv files are converted to two tuple, representing the a key and value, with the following format:
|
|
|
|
* **key**: `{gender, unit, t-value}`
|
|
* **value**: map with the keys:
|
|
* l
|
|
* m
|
|
* s
|
|
* sd3neg
|
|
* sd2neg
|
|
* sd1neg
|
|
* sd0
|
|
* sd1
|
|
* sd2
|
|
* sd3
|
|
* source
|
|
* category
|
|
|
|
"""
|
|
|
|
@spec all :: [[boolean()]]
|
|
@doc """
|
|
Load indicators csv files into their own ets tables.
|
|
"""
|
|
def all do
|
|
"priv/growth/indicators/*.csv"
|
|
|> Path.wildcard()
|
|
|> Enum.map(&create_ets/1)
|
|
|> Enum.map(&Task.async(__MODULE__, :load_measure, [&1]))
|
|
|> Task.await_many()
|
|
end
|
|
|
|
@spec create_ets(String.t()) :: {:atom, String.t()}
|
|
@doc """
|
|
Create a public ets table for the csv filename, using the file name as the table name.
|
|
|
|
Returns a tuple with table name as an atom and the filename.
|
|
"""
|
|
def create_ets(filename) do
|
|
measure = filename |> Path.basename() |> Path.rootname() |> String.to_atom()
|
|
|
|
try do
|
|
:ets.new(measure, [:set, :public, :named_table])
|
|
rescue
|
|
_ ->
|
|
nil
|
|
end
|
|
|
|
{measure, filename}
|
|
end
|
|
|
|
@spec load_measure({:atom, String.t()}) :: [boolean()]
|
|
@doc """
|
|
Read, convert, and load a measure/filename into the proper ets table.
|
|
"""
|
|
def load_measure({measure, filename}) do
|
|
filename
|
|
|> read()
|
|
|> convert()
|
|
|> load(measure)
|
|
end
|
|
|
|
@spec read(String.t()) :: Enumerable.t()
|
|
@doc false
|
|
def read(filename) do
|
|
filename
|
|
|> File.stream!()
|
|
|> IndicatorParser.parse_stream()
|
|
end
|
|
|
|
@spec convert(Enumerable.t()) :: Enumerable.t()
|
|
@doc false
|
|
def convert(data) do
|
|
Stream.map(data, fn [
|
|
source,
|
|
category,
|
|
gender,
|
|
unit,
|
|
t,
|
|
l,
|
|
m,
|
|
s,
|
|
sd3neg,
|
|
sd2neg,
|
|
sd1neg,
|
|
sd0,
|
|
sd1,
|
|
sd2,
|
|
sd3
|
|
] ->
|
|
converted_t =
|
|
if unit in ~w(day week month) do
|
|
as_integer(t)
|
|
else
|
|
as_float(t)
|
|
end
|
|
|
|
key = {gender, unit, converted_t}
|
|
|
|
value = %{
|
|
l: as_float(l),
|
|
m: as_float(m),
|
|
s: as_float(s),
|
|
sd3neg: as_float(sd3neg),
|
|
sd2neg: as_float(sd2neg),
|
|
sd1neg: as_float(sd1neg),
|
|
sd0: as_float(sd0),
|
|
sd1: as_float(sd1),
|
|
sd2: as_float(sd2),
|
|
sd3: as_float(sd3),
|
|
source: source,
|
|
category: category
|
|
}
|
|
|
|
{key, value}
|
|
end)
|
|
end
|
|
|
|
@spec load(Enumerable.t(), atom()) :: [boolean()]
|
|
@doc false
|
|
def load(data, ets_table) do
|
|
Enum.map(data, fn {key, value} ->
|
|
:ets.insert_new(ets_table, {key, value})
|
|
end)
|
|
end
|
|
|
|
defp as_integer(value), do: value |> Integer.parse() |> elem(0)
|
|
|
|
defp as_float(value), do: value |> Float.parse() |> elem(0)
|
|
end
|