import numpy as np from typing import Dict, Tuple, List # EASI component categorization EASI_CATEGORIES = { 'erythema': { 'name': 'Erythema (Redness)', 'conditions': [ 'Post-Inflammatory hyperpigmentation', 'Erythema ab igne', 'Erythema annulare centrifugum', 'Erythema elevatum diutinum', 'Erythema gyratum repens', 'Erythema multiforme', 'Erythema nodosum', 'Flagellate erythema', 'Annular erythema', 'Drug Rash', 'Allergic Contact Dermatitis', 'Irritant Contact Dermatitis', 'Contact dermatitis', 'Acute dermatitis', 'Chronic dermatitis', 'Acute and chronic dermatitis', 'Sunburn', 'Photodermatitis', 'Phytophotodermatitis', 'Rosacea', 'Seborrheic Dermatitis', 'Stasis Dermatitis', 'Perioral Dermatitis', 'Burn erythema of abdominal wall', 'Burn erythema of back of hand', 'Burn erythema of lower leg', 'Cellulitis', 'Infection of skin', 'Viral Exanthem', 'Infected eczema', 'Crusted eczematous dermatitis', 'Inflammatory dermatosis', 'Vasculitis of the skin', 'Leukocytoclastic Vasculitis', 'Cutaneous lupus', 'CD - Contact dermatitis', 'Acute dermatitis, NOS', 'Herpes Simplex', 'Hypersensitivity', 'Impetigo', 'Pigmented purpuric eruption', 'Pityriasis rosea', 'Tinea', 'Tinea Versicolor' ] }, 'induration': { 'name': 'Induration/Papulation (Swelling/Bumps)', 'conditions': [ 'Prurigo nodularis', 'Urticaria', 'Granuloma annulare', 'Morphea', 'Scleroderma', 'Lichen Simplex Chronicus', 'Lichen planus', 'lichenoid eruption', 'Lichen nitidus', 'Lichen spinulosus', 'Lichen striatus', 'Keratosis pilaris', 'Molluscum Contagiosum', 'Verruca vulgaris', 'Folliculitis', 'Acne', 'Hidradenitis', 'Nodular vasculitis', 'Sweet syndrome', 'Necrobiosis lipoidica', 'Basal Cell Carcinoma', 'SCC', 'SCCIS', 'SK', 'ISK', 'Cutaneous T Cell Lymphoma', 'Skin cancer', 'Adnexal neoplasm', 'Insect Bite', 'Milia', 'Miliaria', 'Xanthoma', 'Psoriasis', 'Lichen planus/lichenoid eruption' ] }, 'excoriation': { 'name': 'Excoriation (Scratching Damage)', 'conditions': [ 'Inflicted skin lesions', 'Scabies', 'Abrasion', 'Abrasion of wrist', 'Superficial wound of body region', 'Scrape', 'Animal bite - wound', 'Pruritic dermatitis', 'Prurigo', 'Atopic dermatitis', 'Scab' ] }, 'lichenification': { 'name': 'Lichenification (Skin Thickening)', 'conditions': [ 'Lichenified eczematous dermatitis', 'Acanthosis nigricans', 'Hyperkeratosis of skin', 'HK - Hyperkeratosis', 'Keratoderma', 'Ichthyosis', 'Ichthyosiform dermatosis', 'Chronic eczema', 'Psoriasis', 'Xerosis' ] } } def probability_to_score(prob: float) -> int: """Convert probability to EASI score (0-3)""" if prob < 0.171: return 0 elif prob < 0.238: return 1 elif prob < 0.421: return 2 elif prob < 0.614: return 3 else: return 3 def calculate_easi_scores(predictions: Dict) -> Tuple[Dict, int]: """ Calculate EASI component scores based on condition probabilities Args: predictions: Dictionary containing prediction results Returns: Tuple of (easi_results dict, total_easi_score int) """ easi_results = {} all_condition_probs = predictions['all_condition_probabilities'] for component, category_info in EASI_CATEGORIES.items(): # Find all conditions in this category category_conditions = [] for condition_name, probability in all_condition_probs.items(): # Skip "Eczema" as it should not be included if condition_name.lower() == 'eczema': continue # Check if condition is in category if condition_name in category_info['conditions']: individual_score = probability_to_score(probability) if individual_score > 0: category_conditions.append({ 'condition': condition_name, 'probability': probability, 'individual_score': individual_score }) # Sort by probability category_conditions.sort(key=lambda x: x['probability'], reverse=True) # Calculate component score (sum, capped at 3) component_score = sum(c['individual_score'] for c in category_conditions) component_score = min(component_score, 3) easi_results[component] = { 'name': category_info['name'], 'score': component_score, 'contributing_conditions': category_conditions } # Calculate total EASI score total_easi = sum(result['score'] for result in easi_results.values()) return easi_results, total_easi def format_easi_response(easi_results: Dict, total_easi: int) -> Dict: """ Format EASI results for API response Args: easi_results: EASI calculation results total_easi: Total EASI score Returns: Formatted dictionary for JSON response """ return { 'total_score': total_easi, 'components': { 'erythema': easi_results['erythema']['score'], 'induration': easi_results['induration']['score'], 'excoriation': easi_results['excoriation']['score'], 'lichenification': easi_results['lichenification']['score'] }, 'severity': get_severity_level(total_easi), 'component_details': { component: { 'name': data['name'], 'score': data['score'], 'contributing_conditions': [ { 'condition': c['condition'], 'probability': round(c['probability'], 4), 'contribution': c['individual_score'] } for c in data['contributing_conditions'] ] } for component, data in easi_results.items() } } def get_severity_level(total_easi: int) -> str: """Get severity level description from EASI score""" if total_easi == 0: return "No significant EASI features detected" elif total_easi <= 3: return "Mild EASI severity" elif total_easi <= 6: return "Moderate EASI severity" elif total_easi <= 9: return "Severe EASI severity" else: return "Very Severe EASI severity"