/*
Neonatal jaundice treatment threshold calculator
Copyright (C) 2024 Lee Yingtong Li
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
// ----------------------
// Threshold calculations
function phototherapy_38wks(d) {
if (d <= 0) {
return 100;
}
if (d <= 1) {
return d * 100 + 100;
}
if (d <= 4) {
return (d - 1) * 150 / 3 + 200;
}
return 350;
}
function exchange_38wks(d) {
if (d <= 0) {
return 100;
}
if (d <= 1.75) {
return d * 350 / 1.75 + 100;
}
return 450;
}
function phototherapy_thresh(d, gestation) {
if (gestation >= 38) {
return phototherapy_38wks(d);
}
if (gestation < 23) {
throw new Error('Invalid gestation');
}
if (d <= 0) {
return 40;
}
let thresh_at_3days = gestation * 10 - 100;
if (d <= 3) {
return d * (thresh_at_3days - 40) / 3 + 40;
}
return thresh_at_3days;
}
function exchange_thresh(d, gestation) {
if (gestation >= 38) {
return exchange_38wks(d);
}
if (gestation < 23) {
throw new Error('Invalid gestation');
}
if (d <= 0) {
return 80;
}
let thresh_at_3days = gestation * 10;
if (d <= 3) {
return d * (thresh_at_3days - 80) / 3 + 80;
}
return thresh_at_3days;
}
// ----------------------------------
// Bilirubin vs threshold calculation
function describeBilirubin(gestation, dateBirth, dateMeasurement, bilirubin) {
// Chronological age in days
let d = (dateMeasurement.getTime() - dateBirth.getTime()) / 1000 / 60 / 60 / 24;
let phototherapy_thresh_value = phototherapy_thresh(d, gestation);
let exchange_thresh_value = exchange_thresh(d, gestation);
// Restate colours separately so Tailwind can detect them
let result_text, text_colour, background_colour, accent_colour;
if (bilirubin < phototherapy_thresh_value) {
if (bilirubin < phototherapy_thresh_value - 50) {
text_colour = 'text-sky-800';
background_colour = 'bg-sky-50';
accent_colour = 'border-sky-400';
} else {
text_colour = 'text-yellow-800';
background_colour = 'bg-yellow-50';
accent_colour = 'border-yellow-400';
}
result_text = 'Bilirubin of ' + bilirubin + ' at ' + prettyPrintDays(d) + ' is ' + prettyPrintBilirubin(phototherapy_thresh_value - bilirubin) + ' below the phototherapy threshold.';
} else if (bilirubin < exchange_thresh_value) {
text_colour = 'text-orange-800';
background_colour = 'bg-orange-50';
accent_colour = 'border-orange-400';
result_text = 'Bilirubin of ' + bilirubin + ' at ' + prettyPrintDays(d) + ' is ' + prettyPrintBilirubin(bilirubin - phototherapy_thresh_value) + ' above the phototherapy threshold, ' + prettyPrintBilirubin(exchange_thresh_value - bilirubin) + ' below the exchange transfusion threshold.';
} else {
text_colour = 'text-red-800';
background_colour = 'bg-red-50';
accent_colour = 'border-red-400';
result_text = 'Bilirubin of ' + bilirubin + ' at ' + prettyPrintDays(d) + ' is ' + prettyPrintBilirubin(bilirubin - exchange_thresh_value) + ' above the exchange transfusion threshold.';
}
return [d, result_text, text_colour, background_colour, accent_colour];
}
// --------------
// Utility functions
function prettyPrintHours(d) {
if (d >= 23.5/24) {
throw new Error('>24 hours passed to prettyPrintHours');
}
if (d < 0.5/24) {
return '0 hours';
}
if (d < 1.5/24) {
return '1 hour';
}
return (d * 24).toFixed(0) + ' hours';
}
function prettyPrintDays(d) {
if (d < 23.5/24) {
return prettyPrintHours(d);
}
if (d < 1 + 23.5/24) {
return '1 day, ' + prettyPrintHours(d % 1);
}
let d_rounded = Math.round(d * 24) / 24; // Round 23.5 hours+ to the next day
return Math.floor(d_rounded) + ' days, ' + prettyPrintHours(d_rounded % 1);
}
function prettyPrintBilirubin(b) {
if (b < 10) {
return b.toFixed(1);
} else {
return Math.round(b).toFixed(0);
}
}
function dateToISOStringLocal(date) {
function pad(n) {
if (n < 10) {
return '0' + n;
}
return '' + n;
}
return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()) + 'T' + pad(date.getHours()) + ':' + pad(date.getMinutes());
}