2024-06-09 20:00:49 +00:00
|
|
|
defmodule Growth do
|
|
|
|
@moduledoc """
|
|
|
|
Follow up child growth from 0 to 19 years, calculating z-scores for the following measurements:
|
|
|
|
|
|
|
|
* weight between ages 0 and 10 years
|
|
|
|
* height between ages 0 and 19 years
|
|
|
|
* body mass index (bmi) between ages 0 and 19 years
|
|
|
|
* head circumference between ages 0 and 5 years
|
|
|
|
* arm circumference between ages 3 months and 5 years
|
|
|
|
* subscapular skinfold between ages 3 months and 5 years
|
|
|
|
* triceps skinfold between ages 3 months and 5 years
|
|
|
|
"""
|
|
|
|
|
|
|
|
alias __MODULE__.Calc.Age
|
|
|
|
alias __MODULE__.Calc.BMI
|
2024-06-11 11:42:57 +00:00
|
|
|
alias __MODULE__.Score
|
2024-06-09 20:00:49 +00:00
|
|
|
|
|
|
|
@type gender :: :male | :female
|
|
|
|
|
|
|
|
@type measure :: number() | nil
|
|
|
|
|
|
|
|
@type opts :: [
|
|
|
|
date_of_measurement: Date.t(),
|
|
|
|
weight: measure(),
|
|
|
|
height: measure(),
|
|
|
|
arm_circumference: measure(),
|
|
|
|
head_circumference: measure(),
|
|
|
|
subscapular_skinfold: measure(),
|
|
|
|
triceps_skinfold: measure()
|
|
|
|
]
|
|
|
|
|
|
|
|
@type t :: %__MODULE__{
|
|
|
|
name: String.t(),
|
|
|
|
gender: gender(),
|
|
|
|
date_of_birth: Date.t(),
|
|
|
|
date_of_measurement: Date.t(),
|
|
|
|
age_in_days: pos_integer(),
|
|
|
|
age_in_weeks: pos_integer(),
|
|
|
|
age_in_months: pos_integer(),
|
|
|
|
weight: measure(),
|
|
|
|
height: measure(),
|
|
|
|
arm_circumference: measure(),
|
|
|
|
head_circumference: measure(),
|
|
|
|
subscapular_skinfold: measure(),
|
|
|
|
triceps_skinfold: measure(),
|
|
|
|
bmi: measure(),
|
|
|
|
results: list()
|
|
|
|
}
|
|
|
|
|
|
|
|
@enforce_keys [:name, :gender, :date_of_birth]
|
|
|
|
defstruct [
|
|
|
|
:name,
|
|
|
|
:gender,
|
|
|
|
:date_of_birth,
|
|
|
|
:date_of_measurement,
|
|
|
|
:age_in_days,
|
|
|
|
:age_in_weeks,
|
|
|
|
:age_in_months,
|
|
|
|
:weight,
|
|
|
|
:height,
|
|
|
|
:arm_circumference,
|
|
|
|
:head_circumference,
|
|
|
|
:subscapular_skinfold,
|
|
|
|
:triceps_skinfold,
|
|
|
|
:bmi,
|
|
|
|
:results
|
|
|
|
]
|
|
|
|
|
|
|
|
@valid_measures [
|
|
|
|
:date_of_measurement,
|
|
|
|
:weight,
|
|
|
|
:height,
|
|
|
|
:arm_circumference,
|
|
|
|
:head_circumference,
|
|
|
|
:subscapular_skinfold,
|
|
|
|
:triceps_skinfold
|
|
|
|
]
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
Create a new growth measurement for a children with name, gender, date of birth, and the following optional arguments:
|
|
|
|
|
|
|
|
* `:date_of_measurement`: date when the measures were collected, defaults to today.
|
|
|
|
* `:weight`: weight in kilograms, defaults to `nil`.
|
|
|
|
* `:height`: height in centimeters, defaults to `nil`.
|
|
|
|
* `:arm_circumference`: arm circumference in centimeters, defaults to `nil`.
|
|
|
|
* `:head_circumference`: head circumference in centimeters, defaults to `nil`.
|
|
|
|
* `:subscapular_skinfold`: subscapular skinfold in milimeters, defaults to `nil`.
|
|
|
|
* `:triceps_skinfold`: triceps skinfold in milimeters, defaults to `nil`.
|
|
|
|
|
|
|
|
"""
|
|
|
|
@spec new(String.t(), gender(), Date.t(), opts()) :: t()
|
|
|
|
def new(name, gender, date_of_birth, opts \\ []) do
|
|
|
|
default_opts()
|
|
|
|
|> Enum.reduce(
|
|
|
|
%__MODULE__{
|
|
|
|
name: name,
|
|
|
|
gender: gender,
|
2024-06-10 18:35:12 +00:00
|
|
|
date_of_birth: date_of_birth,
|
|
|
|
results: []
|
2024-06-09 20:00:49 +00:00
|
|
|
},
|
|
|
|
fn {key, default_value}, measurement ->
|
|
|
|
opts
|
|
|
|
|> Keyword.get(key, default_value)
|
|
|
|
|> then(&Map.put(measurement, key, &1))
|
|
|
|
end
|
|
|
|
)
|
|
|
|
|> with_age_in_days()
|
|
|
|
|> with_age_in_weeks()
|
|
|
|
|> with_age_in_months()
|
|
|
|
|> with_bmi()
|
2024-06-11 11:42:57 +00:00
|
|
|
|> with_results()
|
2024-06-09 20:00:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
for precision <- [:day, :week, :month] do
|
|
|
|
@doc """
|
|
|
|
Add age with given precision in growth measurement.
|
|
|
|
"""
|
|
|
|
@spec unquote(:"with_age_in_#{precision}s")(t()) :: t()
|
|
|
|
|
|
|
|
def unquote(:"with_age_in_#{precision}s")(
|
|
|
|
%__MODULE__{date_of_birth: date_of_birth, date_of_measurement: date_of_measurement} =
|
|
|
|
growth
|
|
|
|
)
|
|
|
|
when not is_nil(date_of_birth) and not is_nil(date_of_measurement) do
|
|
|
|
age = Age.calculate(unquote(precision), date_of_birth, date_of_measurement)
|
|
|
|
Map.put(growth, unquote(:"age_in_#{precision}s"), age)
|
|
|
|
end
|
|
|
|
|
|
|
|
def unquote(:"with_age_in_#{precision}s")(%__MODULE__{date_of_birth: date_of_birth} = growth)
|
|
|
|
when not is_nil(date_of_birth) do
|
|
|
|
unquote(:"with_age_in_#{precision}s")(%{growth | date_of_measurement: Date.utc_today()})
|
|
|
|
end
|
|
|
|
|
|
|
|
def unquote(:"with_age_in_#{precision}s")(growth) do
|
|
|
|
growth
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def with_bmi(%__MODULE__{weight: weight, height: height} = growth)
|
|
|
|
when is_number(weight) and is_number(height) do
|
|
|
|
%{growth | bmi: BMI.calculate(:metric, weight, height)}
|
|
|
|
end
|
|
|
|
|
|
|
|
def with_bmi(growth) do
|
|
|
|
growth
|
|
|
|
end
|
|
|
|
|
2024-06-11 11:42:57 +00:00
|
|
|
def with_results(growth) do
|
|
|
|
Score.Scorer.results(growth, [
|
|
|
|
Score.BMI,
|
|
|
|
Score.Height,
|
|
|
|
Score.Weight,
|
|
|
|
Score.TricepsSkinfold,
|
|
|
|
Score.SubscapularSkinfold,
|
|
|
|
Score.ArmCircumference,
|
|
|
|
Score.HeadCircumference
|
|
|
|
])
|
|
|
|
end
|
|
|
|
|
2024-06-09 20:00:49 +00:00
|
|
|
defp default_opts do
|
|
|
|
Enum.map(@valid_measures, fn
|
|
|
|
:date_of_measurement = key ->
|
|
|
|
{key, Date.utc_today()}
|
|
|
|
|
|
|
|
key ->
|
|
|
|
{key, nil}
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|