///|
pub(all) struct MeasurementStats {
count : Int
missing : Int
minimum : Double
maximum : Double
sum : Double
mean : Double
units : Map[String, Int]
} derive(Debug, Eq, ToJson)
///|
pub fn observation_measurement(value : Json) -> Double? {
number_at(value, "valueQuantity.value")
}
///|
pub fn observation_measurement_unit(value : Json) -> String {
quantity_unit(value, "valueQuantity").unwrap_or("")
}
///|
pub fn observation_measurement_stats(values : Array[Json]) -> MeasurementStats {
let units : Map[String, Int] = Map([])
let mut count = 0
let mut missing = 0
let mut minimum = 0.0
let mut maximum = 0.0
let mut sum = 0.0
for value in values {
match observation_measurement(value) {
None => missing += 1
Some(number) => {
if count == 0 {
minimum = number
maximum = number
} else {
if number < minimum {
minimum = number
}
if number > maximum {
maximum = number
}
}
count += 1
sum += number
let unit = observation_measurement_unit(value)
units[unit] = units.get(unit).unwrap_or(0) + 1
}
}
}
let mean = if count == 0 { 0.0 } else { sum / count.to_double() }
{ count, missing, minimum, maximum, sum, mean, units }
}
///|
pub fn observation_values(values : Array[Json]) -> Array[Double] {
values.filter_map(observation_measurement)
}
///|
pub fn observation_measurement_range(values : Array[Json]) -> (Double, Double)? {
let stats = observation_measurement_stats(values)
if stats.count == 0 {
None
} else {
Some((stats.minimum, stats.maximum))
}
}
///|
pub fn observation_units(values : Array[Json]) -> Map[String, Int] {
observation_measurement_stats(values).units
}
///|
pub fn group_observations_by_code(
values : Array[Json],
) -> Map[String, Array[Json]] {
let grouped : Map[String, Array[Json]] = Map([])
for value in values {
let code = query_text(
value,
"code.coding[0].code",
fallback=query_text(value, "code.text", fallback=""),
)
match grouped.get(code) {
None => grouped[code] = [value]
Some(items) => items.push(value)
}
}
grouped
}
///|
pub fn observation_code_count(values : Array[Json], code : String) -> Int {
group_observations_by_code(values)
.get(code)
.map(fn(items) { items.length() })
.unwrap_or(0)
}
///|
pub fn normalise_quantity(value : Json) -> Json {
match query_first(value, "valueQuantity") {
Some(Object(fields)) => {
let output : Map[String, Json] = Map([])
match fields.get("value") {
Some(number) => output["value"] = number
None => ()
}
match fields.get("unit") {
Some(unit) => output["unit"] = unit
None => ()
}
match fields.get("system") {
Some(system) => output["system"] = system
None => ()
}
Json::object(output)
}
_ => Json::empty_object()
}
}
///|
pub fn measurement_summary(values : Array[Json]) -> String {
let stats = observation_measurement_stats(values)
"count=\{stats.count} missing=\{stats.missing} min=\{stats.minimum} max=\{stats.maximum} mean=\{stats.mean}"
}
///|
pub fn measurement_values_in_range(
values : Array[Json],
minimum : Double,
maximum : Double,
) -> Array[Json] {
values.filter(fn(value) {
match observation_measurement(value) {
Some(number) => number >= minimum && number <= maximum
None => false
}
})
}
///|
pub fn measurement_values_with_unit(
values : Array[Json],
unit : String,
) -> Array[Json] {
values.filter(fn(value) { observation_measurement_unit(value) == unit })
}