File size: 12,944 Bytes
a04c9e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f32824f
 
 
 
 
 
 
 
 
 
 
 
 
a04c9e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
"""
Clinical Calculator Suite - Phase 1.2 MCP Development
Implements common clinical calculations for pharmacist workflow
"""

import logging
from typing import Dict, Any

logger = logging.getLogger(__name__)

def cockcroft_gault_creatinine_clearance(
    age: int,
    weight_kg: float,
    serum_creatinine_mg_dl: float,
    is_female: bool = False
) -> Dict[str, Any]:
    """
    Calculate creatinine clearance using Cockcroft-Gault equation.
    
    Args:
        age: Patient age in years
        weight_kg: Weight in kilograms
        serum_creatinine_mg_dl: Serum creatinine in mg/dL
        is_female: True if patient is female
    
    Returns:
        Dict with calculated creatinine clearance and interpretation
    """
    # Enhanced input validation with specific error messages
    if age <= 0:
        raise ValueError(f"Age must be positive, got {age}")
    if age > 120:
        raise ValueError(f"Age seems unrealistic, got {age}")
    if weight_kg <= 0:
        raise ValueError(f"Weight must be positive, got {weight_kg}")
    if weight_kg > 500:
        raise ValueError(f"Weight seems unrealistic, got {weight_kg}")
    if serum_creatinine_mg_dl <= 0:
        raise ValueError(f"Serum creatinine must be positive, got {serum_creatinine_mg_dl}")
    if serum_creatinine_mg_dl > 20:
        raise ValueError(f"Serum creatinine seems unrealistic, got {serum_creatinine_mg_dl}")
    
    clearance = ((140 - age) * weight_kg) / (72 * serum_creatinine_mg_dl)
    
    if is_female:
        clearance *= 0.85
    
    if clearance >= 90:
        stage = "Normal or high"
        category = "G1"
    elif clearance >= 60:
        stage = "Mildly decreased"
        category = "G2"
    elif clearance >= 45:
        stage = "Mild to moderately decreased"
        category = "G3a"
    elif clearance >= 30:
        stage = "Moderately to severely decreased"
        category = "G3b"
    elif clearance >= 15:
        stage = "Severely decreased"
        category = "G4"
    else:
        stage = "Kidney failure"
        category = "G5"
    
    return {
        "creatinine_clearance_ml_min": round(clearance, 1),
        "kidney_function_stage": stage,
        "gfr_category": category,
        "formula_used": "Cockcroft-Gault",
        "requires_dose_adjustment": clearance < 60,
        "patient_info": {
            "age": age,
            "weight_kg": weight_kg,
            "serum_creatinine_mg_dl": serum_creatinine_mg_dl,
            "is_female": is_female
        }
    }

def ckd_epi_egfr(
    age: int,
    serum_creatinine_mg_dl: float,
    is_female: bool = False,
    is_black: bool = False
) -> Dict[str, Any]:
    """
    Calculate estimated GFR using CKD-EPI equation.
    
    Args:
        age: Patient age in years
        serum_creatinine_mg_dl: Serum creatinine in mg/dL
        is_female: True if patient is female
        is_black: True if patient is Black
    
    Returns:
        Dict with calculated eGFR and interpretation
    """
    if age <= 0 or serum_creatinine_mg_dl <= 0:
        raise ValueError("Age and creatinine must be positive")
    
    if is_female:
        kappa = 0.7
        alpha = -0.329
        if serum_creatinine_mg_dl <= kappa:
            alpha = -0.411
    else:
        kappa = 0.9
        alpha = -0.411
        if serum_creatinine_mg_dl <= kappa:
            alpha = -0.302
    
    scr_kappa_ratio = serum_creatinine_mg_dl / kappa
    if serum_creatinine_mg_dl <= kappa:
        egfr = 141 * (scr_kappa_ratio ** alpha) * (0.993 ** age)
    else:
        egfr = 141 * (scr_kappa_ratio ** -1.209) * (0.993 ** age)
    
    if is_female:
        egfr *= 1.018
    
    if is_black:
        egfr *= 1.159
    
    if egfr >= 90:
        stage = "Normal or high"
        category = "G1"
    elif egfr >= 60:
        stage = "Mildly decreased"
        category = "G2"
    elif egfr >= 45:
        stage = "Mild to moderately decreased"
        category = "G3a"
    elif egfr >= 30:
        stage = "Moderately to severely decreased"
        category = "G3b"
    elif egfr >= 15:
        stage = "Severely decreased"
        category = "G4"
    else:
        stage = "Kidney failure"
        category = "G5"
    
    return {
        "egfr_ml_min_1_73m2": round(egfr, 1),
        "kidney_function_stage": stage,
        "gfr_category": category,
        "formula_used": "CKD-EPI",
        "requires_dose_adjustment": egfr < 60,
        "patient_info": {
            "age": age,
            "serum_creatinine_mg_dl": serum_creatinine_mg_dl,
            "is_female": is_female,
            "is_black": is_black
        }
    }

def child_pugh_score(
    bilirubin_mg_dl: float,
    albumin_g_dl: float,
    inr: float,
    ascites: str,
    encephalopathy: str
) -> Dict[str, Any]:
    """
    Calculate Child-Pugh score for liver function assessment.
    
    Args:
        bilirubin_mg_dl: Total bilirubin in mg/dL
        albumin_g_dl: Serum albumin in g/dL
        inr: International Normalized Ratio
        ascites: 'none', 'mild', or 'moderate-severe'
        encephalopathy: 'none', 'grade-1-2', or 'grade-3-4'
    
    Returns:
        Dict with Child-Pugh score, class, and interpretation
    """
    score = 0
    
    if bilirubin_mg_dl < 2:
        score += 1
    elif bilirubin_mg_dl <= 3:
        score += 2
    else:
        score += 3
    
    if albumin_g_dl > 3.5:
        score += 1
    elif albumin_g_dl >= 2.8:
        score += 2
    else:
        score += 3
    
    if inr < 1.7:
        score += 1
    elif inr <= 2.3:
        score += 2
    else:
        score += 3
    
    ascites_lower = ascites.lower()
    if 'none' in ascites_lower:
        score += 1
    elif 'mild' in ascites_lower:
        score += 2
    else:
        score += 3
    
    encephalopathy_lower = encephalopathy.lower()
    if 'none' in encephalopathy_lower:
        score += 1
    elif 'grade-1-2' in encephalopathy_lower or '1-2' in encephalopathy_lower:
        score += 2
    else:
        score += 3
    
    if score <= 6:
        child_class = "A"
        mortality_1yr = "< 10%"
        mortality_2yr = "< 15%"
        perioperative_mortality = "10%"
    elif score <= 9:
        child_class = "B"
        mortality_1yr = "10-20%"
        mortality_2yr = "20-30%"
        perioperative_mortality = "30%"
    else:
        child_class = "C"
        mortality_1yr = "> 20%"
        mortality_2yr = "> 35%"
        perioperative_mortality = "50%"
    
    return {
        "child_pugh_score": score,
        "child_pugh_class": child_class,
        "one_year_mortality": mortality_1yr,
        "two_year_mortality": mortality_2yr,
        "perioperative_mortality": perioperative_mortality,
        "requires_dose_adjustment": child_class in ["B", "C"],
        "severe_impairment": child_class == "C",
        "components": {
            "bilirubin_mg_dl": bilirubin_mg_dl,
            "albumin_g_dl": albumin_g_dl,
            "inr": inr,
            "ascites": ascites,
            "encephalopathy": encephalopathy
        }
    }

def bmi_calculator(
    weight_kg: float,
    height_cm: float
) -> Dict[str, Any]:
    """
    Calculate Body Mass Index and provide interpretation.
    
    Args:
        weight_kg: Weight in kilograms
        height_cm: Height in centimeters
    
    Returns:
        Dict with BMI and weight category
    """
    if weight_kg <= 0 or height_cm <= 0:
        raise ValueError("Weight and height must be positive")
    
    height_m = height_cm / 100
    bmi = weight_kg / (height_m ** 2)
    
    if bmi < 18.5:
        category = "Underweight"
        risk = "Increased risk of malnutrition"
    elif bmi < 25:
        category = "Normal weight"
        risk = "Low risk"
    elif bmi < 30:
        category = "Overweight"
        risk = "Increased risk"
    elif bmi < 35:
        category = "Obesity Class I"
        risk = "High risk"
    elif bmi < 40:
        category = "Obesity Class II"
        risk = "Very high risk"
    else:
        category = "Obesity Class III"
        risk = "Extremely high risk"
    
    return {
        "bmi": round(bmi, 1),
        "category": category,
        "health_risk": risk,
        "weight_kg": weight_kg,
        "height_cm": height_cm
    }

def ideal_body_weight(
    height_cm: float,
    is_male: bool = True
) -> Dict[str, Any]:
    """
    Calculate Ideal Body Weight using Devine formula.
    
    Args:
        height_cm: Height in centimeters
        is_male: True if patient is male
    
    Returns:
        Dict with ideal body weight
    """
    if height_cm <= 0:
        raise ValueError("Height must be positive")
    
    height_inches = height_cm / 2.54
    
    if is_male:
        ibw_kg = 50 + 2.3 * (height_inches - 60)
    else:
        ibw_kg = 45.5 + 2.3 * (height_inches - 60)
    
    ibw_kg = max(ibw_kg, 30)
    
    return {
        "ideal_body_weight_kg": round(ibw_kg, 1),
        "height_cm": height_cm,
        "is_male": is_male,
        "formula_used": "Devine"
    }

def adjusted_body_weight(
    actual_weight_kg: float,
    ideal_weight_kg: float,
    correction_factor: float = 0.4
) -> Dict[str, Any]:
    """
    Calculate Adjusted Body Weight for obese patients.
    
    Args:
        actual_weight_kg: Actual body weight in kg
        ideal_weight_kg: Ideal body weight in kg
        correction_factor: Correction factor (default 0.4)
    
    Returns:
        Dict with adjusted body weight
    """
    if actual_weight_kg <= 0 or ideal_weight_kg <= 0:
        raise ValueError("Weights must be positive")
    
    if actual_weight_kg <= ideal_weight_kg * 1.2:
        adjusted_weight = actual_weight_kg
        adjustment_needed = False
    else:
        adjusted_weight = ideal_weight_kg + correction_factor * (actual_weight_kg - ideal_weight_kg)
        adjustment_needed = True
    
    return {
        "adjusted_body_weight_kg": round(adjusted_weight, 1),
        "actual_weight_kg": actual_weight_kg,
        "ideal_weight_kg": ideal_weight_kg,
        "correction_factor": correction_factor,
        "adjustment_needed": adjustment_needed,
        "percent_above_ideal": round(((actual_weight_kg / ideal_weight_kg) - 1) * 100, 1)
    }

def creatinine_conversion(
    creatinine_value: float,
    from_unit: str,
    to_unit: str
) -> Dict[str, Any]:
    """
    Convert creatinine between mg/dL and μmol/L.
    
    Args:
        creatinine_value: Creatinine value to convert
        from_unit: 'mg_dl' or 'umol_l'
        to_unit: 'mg_dl' or 'umol_l'
    
    Returns:
        Dict with converted value
    """
    if creatinine_value <= 0:
        raise ValueError("Creatinine value must be positive")
    
    conversion_factor = 88.42
    
    if from_unit == to_unit:
        converted_value = creatinine_value
    elif from_unit == 'mg_dl' and to_unit == 'umol_l':
        converted_value = creatinine_value * conversion_factor
    elif from_unit == 'umol_l' and to_unit == 'mg_dl':
        converted_value = creatinine_value / conversion_factor
    else:
        raise ValueError("Invalid units. Use 'mg_dl' or 'umol_l'")
    
    return {
        "original_value": creatinine_value,
        "original_unit": from_unit,
        "converted_value": round(converted_value, 2),
        "converted_unit": to_unit,
        "conversion_factor": conversion_factor
    }

def dosing_weight_recommendation(
    actual_weight_kg: float,
    height_cm: float,
    is_male: bool = True
) -> Dict[str, Any]:
    """
    Recommend appropriate weight for dosing calculations.
    
    Args:
        actual_weight_kg: Actual body weight in kg
        height_cm: Height in centimeters
        is_male: True if patient is male
    
    Returns:
        Dict with dosing weight recommendation
    """
    ibw_result = ideal_body_weight(height_cm, is_male)
    ibw = ibw_result["ideal_body_weight_kg"]
    
    bmi_result = bmi_calculator(actual_weight_kg, height_cm)
    bmi = bmi_result["bmi"]
    
    if actual_weight_kg <= ibw * 1.2:
        dosing_weight = actual_weight_kg
        recommendation = "Use actual body weight"
        rationale = "Patient weight is within 20% of ideal body weight"
    elif bmi >= 30:
        adj_weight_result = adjusted_body_weight(actual_weight_kg, ibw)
        dosing_weight = adj_weight_result["adjusted_body_weight_kg"]
        recommendation = "Use adjusted body weight"
        rationale = "Patient is obese (BMI ≥ 30); adjusted weight recommended for most drugs"
    else:
        dosing_weight = actual_weight_kg
        recommendation = "Use actual body weight (consider drug-specific guidelines)"
        rationale = "Patient is overweight but not obese; actual weight typically appropriate"
    
    return {
        "recommended_dosing_weight_kg": dosing_weight,
        "recommendation": recommendation,
        "rationale": rationale,
        "actual_weight_kg": actual_weight_kg,
        "ideal_weight_kg": ibw,
        "bmi": bmi,
        "bmi_category": bmi_result["category"]
    }