feat(growth): add struct to represent measurement
All checks were successful
continuous-integration/drone/pr Build is passing

This commit is contained in:
João Paulo Dubas 2024-06-09 20:00:49 +00:00
parent d3c1aa9b6b
commit 412250d7ef
Signed by: joao.dubas
SSH Key Fingerprint: SHA256:V1mixgOGRc/YMhGx/DNkOSmJxgA2vHNrDZEk3wt/kOA

154
lib/growth/growth.ex Normal file
View File

@ -0,0 +1,154 @@
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
@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,
date_of_birth: date_of_birth
},
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()
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
defp default_opts do
Enum.map(@valid_measures, fn
:date_of_measurement = key ->
{key, Date.utc_today()}
key ->
{key, nil}
end)
end
end