/* 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()); }