import json import os from typing import Dict, Any, Optional import logging # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Global variables _condition_data = {} _last_load_time = 0 def _load_condition_data() -> Dict[str, Any]: """Load skin condition data from JSON file with proper error handling""" json_path = 'data/skin_conditions.json' try: # Get file modification time to check for changes mtime = os.path.getmtime(json_path) # If we've already loaded and file hasn't changed, return cached data global _condition_data, _last_load_time if _condition_data and mtime <= _last_load_time: return _condition_data # Load and parse JSON file with open(json_path, encoding='utf-8') as f: data = json.load(f) # Index data by lowercase condition name for faster lookups condition_map = {} for entry in data: condition = entry.get("condition", "").strip().lower() if condition: # Only add entries with a valid condition name condition_map[condition] = entry # Update cache _condition_data = condition_map _last_load_time = mtime logger.info(f"Loaded {len(condition_map)} skin conditions from {json_path}") return condition_map except FileNotFoundError: logger.error(f"Skin conditions file not found: {json_path}") return {} except json.JSONDecodeError as e: logger.error(f"Invalid JSON in skin conditions file: {e}") return {} except Exception as e: logger.error(f"Error loading skin conditions: {e}") return {} def get_recommended_products(condition_query: str) -> Dict[str, Any]: """ Get recommended products for a given skin condition. Args: condition_query: The skin condition to look up Returns: Dictionary containing condition info and product recommendations, or empty dict if no match found """ if not condition_query: return {} # Standardize the query condition_query = condition_query.strip().lower() # Load or refresh condition data condition_data = _load_condition_data() # Try exact match first (most reliable) if condition_query in condition_data: return condition_data[condition_query] # If exact match fails, try to find the closest match # This is a fallback for when condition names might have slight variations for cond_name, entry in condition_data.items(): # Check if query is a substring of a condition or vice versa if condition_query in cond_name or cond_name in condition_query: return entry # No match found logger.warning(f"No recommendations found for condition: {condition_query}") return {}