Spaces:
Runtime error
Runtime error
| import os | |
| import requests | |
| import logging | |
| import random | |
| from typing import Dict, Any, Optional, List | |
| from dotenv import load_dotenv | |
| # Load environment variables | |
| load_dotenv() | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Get API key from environment | |
| UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY") | |
| UNSPLASH_SECRET_KEY = os.environ.get("UNSPLASH_SECRET_KEY") | |
| # Function to get the singleton instance | |
| def get_image_provider() -> 'ImageIntegration': | |
| return image_provider | |
| class ImageIntegration: | |
| """ | |
| Class to handle image retrieval for articulation practice words | |
| Uses Unsplash API to fetch relevant images | |
| """ | |
| def __init__(self): | |
| self.has_unsplash_api = bool(UNSPLASH_ACCESS_KEY) | |
| if self.has_unsplash_api: | |
| logger.info("Unsplash API key found and configured") | |
| else: | |
| logger.warning("No Unsplash API key found. Using fallback image sources.") | |
| def get_image_for_word(self, word: str) -> Dict[str, Any]: | |
| """ | |
| Get an image for a specific word | |
| Args: | |
| word: The word to find an image for | |
| Returns: | |
| Dictionary with image URL and attribution information | |
| """ | |
| if self.has_unsplash_api: | |
| try: | |
| return self._get_unsplash_image(word) | |
| except Exception as e: | |
| logger.error(f"Error getting Unsplash image for '{word}': {str(e)}") | |
| return self._get_fallback_image(word) | |
| else: | |
| return self._get_fallback_image(word) | |
| def _get_unsplash_image(self, query: str) -> Dict[str, Any]: | |
| """ | |
| Get an image from Unsplash API | |
| Args: | |
| query: Search term for the image | |
| Returns: | |
| Dictionary with image data | |
| """ | |
| try: | |
| url = "https://api.unsplash.com/search/photos" | |
| params = { | |
| "query": query, | |
| "per_page": 1, | |
| "orientation": "landscape", | |
| "content_filter": "high" # Only get high-quality images appropriate for all users | |
| } | |
| headers = { | |
| "Authorization": f"Client-ID {UNSPLASH_ACCESS_KEY}" | |
| } | |
| response = requests.get(url, params=params, headers=headers) | |
| response.raise_for_status() | |
| data = response.json() | |
| if "results" in data and len(data["results"]) > 0: | |
| result = data["results"][0] | |
| # Extract relevant image data | |
| image_data = { | |
| "url": result["urls"]["regular"], | |
| "thumb_url": result["urls"]["thumb"], | |
| "attribution": f"Photo by {result['user']['name']} on Unsplash", | |
| "attribution_url": result['user']['links']['html'], | |
| "source": "unsplash" | |
| } | |
| return image_data | |
| else: | |
| # If no results, try a more general search | |
| logger.info(f"No results for '{query}', trying more general search") | |
| return self._get_general_category_image(query) | |
| except Exception as e: | |
| logger.error(f"Error in Unsplash API call: {str(e)}") | |
| raise | |
| def _get_general_category_image(self, word: str) -> Dict[str, Any]: | |
| """Get a more general category image when specific word has no results""" | |
| # Map specific words to more general categories that are likely to have images | |
| category_mapping = { | |
| # Animals | |
| "cat": "cat", | |
| "dog": "dog", | |
| "fish": "fish", | |
| # Objects | |
| "chair": "furniture", | |
| "table": "furniture", | |
| "lamp": "lighting", | |
| # Food | |
| "apple": "fruit", | |
| "orange": "fruit", | |
| "banana": "fruit", | |
| # Default categories | |
| "default": ["object", "item", "thing", "photo"] | |
| } | |
| # Try to find a category, or use defaults | |
| category = category_mapping.get(word.lower()) | |
| if not category: | |
| # Check if the word ends with common suffixes and map accordingly | |
| if word.endswith(("ing", "tion", "ment")): | |
| category = "action" | |
| elif word.endswith(("ness", "ity", "ty")): | |
| category = "concept" | |
| else: | |
| # Use a random default category | |
| category = random.choice(category_mapping["default"]) | |
| if isinstance(category, list): | |
| category = random.choice(category) | |
| # Now try with the category | |
| try: | |
| url = "https://api.unsplash.com/search/photos" | |
| params = { | |
| "query": category, | |
| "per_page": 30, # Get more results | |
| "orientation": "landscape" | |
| } | |
| headers = { | |
| "Authorization": f"Client-ID {UNSPLASH_ACCESS_KEY}" | |
| } | |
| response = requests.get(url, params=params, headers=headers) | |
| response.raise_for_status() | |
| data = response.json() | |
| if "results" in data and len(data["results"]) > 0: | |
| # Select a random image from results | |
| result = random.choice(data["results"]) | |
| image_data = { | |
| "url": result["urls"]["regular"], | |
| "thumb_url": result["urls"]["thumb"], | |
| "attribution": f"Photo by {result['user']['name']} on Unsplash", | |
| "attribution_url": result['user']['links']['html'], | |
| "source": "unsplash" | |
| } | |
| return image_data | |
| else: | |
| return self._get_fallback_image(word) | |
| except Exception as e: | |
| logger.error(f"Error in general category search: {str(e)}") | |
| return self._get_fallback_image(word) | |
| def _get_fallback_image(self, word: str) -> Dict[str, Any]: | |
| """ | |
| Get a fallback image when API fails or is not available | |
| Args: | |
| word: The word to get an image for | |
| Returns: | |
| Dictionary with fallback image data | |
| """ | |
| # Use free placeholder services or default images | |
| image_services = [ | |
| # Format: (url_template, attribution) | |
| (f"https://source.unsplash.com/400x400/?{word}", "Image from Unsplash"), | |
| (f"https://placehold.co/400x400/random/ffffff?text={word}", "Generated placeholder image"), | |
| (f"https://dummyimage.com/400x400/3498db/ffffff&text={word}", "Generated placeholder image") | |
| ] | |
| # Select a random service | |
| service = random.choice(image_services) | |
| return { | |
| "url": service[0], | |
| "attribution": service[1], | |
| "source": "fallback" | |
| } | |
| def get_images_for_words(self, words: List[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
| """ | |
| Get images for a list of words | |
| Args: | |
| words: List of word dictionaries | |
| Returns: | |
| Words with added image data | |
| """ | |
| result = [] | |
| for word_item in words: | |
| try: | |
| word = word_item["word"] | |
| image_data = self.get_image_for_word(word) | |
| # Add image data to the word item | |
| word_item["image"] = image_data | |
| result.append(word_item) | |
| except Exception as e: | |
| logger.error(f"Error getting image for word '{word_item.get('word', '')}': {str(e)}") | |
| # Add the word without an image | |
| result.append(word_item) | |
| return result | |
| # Create singleton instance | |
| image_provider = ImageIntegration() |