Update App.js, new modular structure

This commit is contained in:
2025-10-18 21:19:17 +02:00
parent b666b35fed
commit 0fb168b050
16 changed files with 1118 additions and 453 deletions

65
src/utils/calculations.js Normal file
View File

@@ -0,0 +1,65 @@
import { timeToMinutes } from './timeUtils.js';
import { calculateSingleDoseConcentration } from './pharmacokinetics.js';
export const calculateCombinedProfile = (
doseSchedule,
deviationList = [],
correction = null,
steadyStateConfig,
simulationDays,
pkParams
) => {
const dataPoints = [];
const timeStepHours = 0.25;
const totalHours = (parseInt(simulationDays, 10) || 3) * 24;
const daysToSimulate = Math.min(parseInt(steadyStateConfig.daysOnMedication, 10) || 0, 5);
for (let t = 0; t <= totalHours; t += timeStepHours) {
let totalLdx = 0;
let totalDamph = 0;
let allDoses = [];
const maxDayOffset = (parseInt(simulationDays, 10) || 3) - 1;
for (let day = -daysToSimulate; day <= maxDayOffset; day++) {
const dayOffset = day * 24 * 60;
doseSchedule.forEach(d => {
allDoses.push({ ...d, time: timeToMinutes(d.time) + dayOffset, isPlan: true });
});
}
const currentDeviations = [...deviationList];
if (correction) {
currentDeviations.push({ ...correction, isAdditional: true });
}
currentDeviations.forEach(dev => {
const devTime = timeToMinutes(dev.time) + (dev.dayOffset || 0) * 24 * 60;
if (!dev.isAdditional) {
const closestDoseIndex = allDoses.reduce((closest, dose, index) => {
if (!dose.isPlan) return closest;
const diff = Math.abs(dose.time - devTime);
if (diff <= 60 && diff < closest.minDiff) {
return { index, minDiff: diff };
}
return closest;
}, { index: -1, minDiff: 61 }).index;
if (closestDoseIndex !== -1) {
allDoses.splice(closestDoseIndex, 1);
}
}
allDoses.push({ ...dev, time: devTime });
});
allDoses.forEach(doseInfo => {
const timeSinceDoseHours = t - doseInfo.time / 60;
const concentrations = calculateSingleDoseConcentration(doseInfo.dose, timeSinceDoseHours, pkParams);
totalLdx += concentrations.ldx;
totalDamph += concentrations.damph;
});
dataPoints.push({ timeHours: t, ldx: totalLdx, damph: totalDamph });
}
return dataPoints;
};

View File

@@ -0,0 +1,29 @@
import { LDX_TO_DAMPH_CONVERSION_FACTOR } from '../constants/defaults.js';
// Pharmacokinetic calculations
export const calculateSingleDoseConcentration = (dose, timeSinceDoseHours, pkParams) => {
const numDose = parseFloat(dose) || 0;
if (timeSinceDoseHours < 0 || numDose <= 0) return { ldx: 0, damph: 0 };
const ka_ldx = Math.log(2) / (parseFloat(pkParams.ldx.absorptionRate) || 1);
const k_conv = Math.log(2) / (parseFloat(pkParams.ldx.halfLife) || 1);
const ke_damph = Math.log(2) / (parseFloat(pkParams.damph.halfLife) || 1);
let ldxConcentration = 0;
if (Math.abs(ka_ldx - k_conv) > 0.0001) {
ldxConcentration = (numDose * ka_ldx / (ka_ldx - k_conv)) *
(Math.exp(-k_conv * timeSinceDoseHours) - Math.exp(-ka_ldx * timeSinceDoseHours));
}
let damphConcentration = 0;
if (Math.abs(ka_ldx - ke_damph) > 0.0001 &&
Math.abs(k_conv - ke_damph) > 0.0001 &&
Math.abs(ka_ldx - k_conv) > 0.0001) {
const term1 = Math.exp(-ke_damph * timeSinceDoseHours) / ((ka_ldx - ke_damph) * (k_conv - ke_damph));
const term2 = Math.exp(-k_conv * timeSinceDoseHours) / ((ka_ldx - k_conv) * (ke_damph - k_conv));
const term3 = Math.exp(-ka_ldx * timeSinceDoseHours) / ((k_conv - ka_ldx) * (ke_damph - ka_ldx));
damphConcentration = LDX_TO_DAMPH_CONVERSION_FACTOR * numDose * ka_ldx * k_conv * (term1 + term2 + term3);
}
return { ldx: Math.max(0, ldxConcentration), damph: Math.max(0, damphConcentration) };
};

69
src/utils/suggestions.js Normal file
View File

@@ -0,0 +1,69 @@
import { timeToMinutes } from './timeUtils.js';
import { calculateCombinedProfile } from './calculations.js';
export const generateSuggestion = (
doses,
deviations,
doseIncrement,
simulationDays,
steadyStateConfig,
pkParams
) => {
if (deviations.length === 0) {
return null;
}
const lastDeviation = [...deviations].sort((a, b) =>
timeToMinutes(a.time) + (a.dayOffset || 0) * 1440 -
(timeToMinutes(b.time) + (b.dayOffset || 0) * 1440)
).pop();
const deviationTimeTotalMinutes = timeToMinutes(lastDeviation.time) + (lastDeviation.dayOffset || 0) * 1440;
let nextDose = null;
let minDiff = Infinity;
doses.forEach(d => {
const doseTimeInMinutes = timeToMinutes(d.time);
for (let i = 0; i < (parseInt(simulationDays, 10) || 1); i++) {
const absoluteTime = doseTimeInMinutes + i * 1440;
const diff = absoluteTime - deviationTimeTotalMinutes;
if (diff > 0 && diff < minDiff) {
minDiff = diff;
nextDose = { ...d, dayOffset: i };
}
}
});
if (!nextDose) {
return { text: "Keine passende nächste Dosis für Korrektur gefunden." };
}
const numDoseIncrement = parseFloat(doseIncrement) || 1;
const idealProfile = calculateCombinedProfile(doses, [], null, steadyStateConfig, simulationDays, pkParams);
const deviatedProfile = calculateCombinedProfile(doses, deviations, null, steadyStateConfig, simulationDays, pkParams);
const nextDoseTimeHours = (timeToMinutes(nextDose.time) + (nextDose.dayOffset || 0) * 1440) / 60;
const idealConcentration = idealProfile.find(p => Math.abs(p.timeHours - nextDoseTimeHours) < 0.1)?.damph || 0;
const deviatedConcentration = deviatedProfile.find(p => Math.abs(p.timeHours - nextDoseTimeHours) < 0.1)?.damph || 0;
const concentrationDifference = idealConcentration - deviatedConcentration;
if (Math.abs(concentrationDifference) < 0.5) {
return { text: "Keine signifikante Korrektur notwendig." };
}
const doseAdjustmentFactor = 0.5;
let doseChange = concentrationDifference / doseAdjustmentFactor;
doseChange = Math.round(doseChange / numDoseIncrement) * numDoseIncrement;
let suggestedDoseValue = (parseFloat(nextDose.dose) || 0) + doseChange;
suggestedDoseValue = Math.max(0, Math.min(70, suggestedDoseValue));
return {
time: nextDose.time,
dose: String(suggestedDoseValue),
isAdditional: false,
originalDose: nextDose.dose,
dayOffset: nextDose.dayOffset
};
};

6
src/utils/timeUtils.js Normal file
View File

@@ -0,0 +1,6 @@
// --- Helper Functions ---
export const timeToMinutes = (timeStr) => {
if (!timeStr || !timeStr.includes(':')) return 0;
const [hours, minutes] = timeStr.split(':').map(Number);
return hours * 60 + minutes;
};