feat(growth): add struct to represent measurement
All checks were successful
continuous-integration/drone/pr Build is passing
All checks were successful
continuous-integration/drone/pr Build is passing
This commit is contained in:
parent
d3c1aa9b6b
commit
412250d7ef
154
lib/growth/growth.ex
Normal file
154
lib/growth/growth.ex
Normal 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
|
Loading…
x
Reference in New Issue
Block a user