163 lines
3.9 KiB
Elixir
163 lines
3.9 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:
|
|
|
|
* `Growth.Score.ArmCircumference`
|
|
* `Growth.Score.BMI`
|
|
* `Growth.Score.HeadCircumference`
|
|
* `Growth.Score.Height`
|
|
* `Growth.Score.SubscapularSkinfold`
|
|
* `Growth.Score.TricepsSkinfold`
|
|
* `Growth.Score.Weight`
|
|
* `Growth.Score.WeightForHeight`
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
alias Growth.Score
|
|
|
|
@measure_to_score [
|
|
arm_circumference_for_age: Score.ArmCircumference,
|
|
bmi_for_age: Score.BMI,
|
|
head_circumference_for_age: Score.HeadCircumference,
|
|
height_for_age: Score.Height,
|
|
subscapular_skinfold_for_age: Score.SubscapularSkinfold,
|
|
triceps_skinfold_for_age: Score.TricepsSkinfold,
|
|
weight_for_age: Score.Weight,
|
|
weight_for_height: Score.WeightForHeight
|
|
]
|
|
|
|
@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()
|
|
|> then(&Keyword.get(@measure_to_score, &1, &1))
|
|
|
|
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 = {String.to_existing_atom(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
|