""" 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"] }