""" AI Engine utility functions """ from typing import Optional from django.contrib.contenttypes.models import ContentType from .models import SentimentResult def get_sentiment_badge_class(sentiment: str) -> str: """ Get Bootstrap badge class for sentiment. Args: sentiment: Sentiment value ('positive', 'neutral', 'negative') Returns: Bootstrap badge class """ badge_classes = { 'positive': 'bg-success', 'neutral': 'bg-secondary', 'negative': 'bg-danger', } return badge_classes.get(sentiment, 'bg-secondary') def get_sentiment_icon(sentiment: str) -> str: """ Get icon for sentiment. Args: sentiment: Sentiment value ('positive', 'neutral', 'negative') Returns: Icon class or emoji """ icons = { 'positive': '😊', 'neutral': '😐', 'negative': '😞', } return icons.get(sentiment, '😐') def format_sentiment_score(score: float) -> str: """ Format sentiment score for display. Args: score: Sentiment score (-1 to 1) Returns: Formatted score string """ return f"{score:+.2f}" def format_confidence(confidence: float) -> str: """ Format confidence as percentage. Args: confidence: Confidence value (0 to 1) Returns: Formatted percentage string """ return f"{confidence * 100:.1f}%" def get_sentiment_color(sentiment: str) -> str: """ Get color code for sentiment. Args: sentiment: Sentiment value ('positive', 'neutral', 'negative') Returns: Hex color code """ colors = { 'positive': '#28a745', # Green 'neutral': '#6c757d', # Gray 'negative': '#dc3545', # Red } return colors.get(sentiment, '#6c757d') def get_emotion_icon(emotion: str) -> str: """ Get icon/emoji for emotion. Args: emotion: Emotion name Returns: Emoji representing the emotion """ icons = { 'joy': '😄', 'anger': '😠', 'sadness': '😢', 'fear': '😨', 'surprise': '😲', 'disgust': '🤢', 'trust': '🤝', 'anticipation': '🤔', } return icons.get(emotion, '😐') def has_sentiment_analysis(obj) -> bool: """ Check if an object has sentiment analysis. Args: obj: Django model instance Returns: True if sentiment analysis exists """ content_type = ContentType.objects.get_for_model(obj) return SentimentResult.objects.filter( content_type=content_type, object_id=obj.id ).exists() def get_latest_sentiment(obj) -> Optional[SentimentResult]: """ Get the latest sentiment analysis for an object. Args: obj: Django model instance Returns: SentimentResult instance or None """ content_type = ContentType.objects.get_for_model(obj) return SentimentResult.objects.filter( content_type=content_type, object_id=obj.id ).first() def get_sentiment_summary(sentiment_score: float) -> str: """ Get human-readable sentiment summary. Args: sentiment_score: Sentiment score (-1 to 1) Returns: Human-readable summary """ if sentiment_score >= 0.6: return "Very Positive" elif sentiment_score >= 0.2: return "Positive" elif sentiment_score >= -0.2: return "Neutral" elif sentiment_score >= -0.6: return "Negative" else: return "Very Negative" def calculate_sentiment_trend(results: list) -> dict: """ Calculate sentiment trend from a list of results. Args: results: List of SentimentResult instances Returns: Dictionary with trend information """ if not results: return { 'direction': 'stable', 'change': 0, 'description': 'No data' } # Calculate average scores for first and second half mid_point = len(results) // 2 first_half = results[:mid_point] second_half = results[mid_point:] if not first_half or not second_half: return { 'direction': 'stable', 'change': 0, 'description': 'Insufficient data' } first_avg = sum(float(r.sentiment_score) for r in first_half) / len(first_half) second_avg = sum(float(r.sentiment_score) for r in second_half) / len(second_half) change = second_avg - first_avg if change > 0.1: direction = 'improving' description = 'Sentiment is improving' elif change < -0.1: direction = 'declining' description = 'Sentiment is declining' else: direction = 'stable' description = 'Sentiment is stable' return { 'direction': direction, 'change': change, 'description': description } def get_top_keywords(results: list, limit: int = 10) -> list: """ Get top keywords from a list of sentiment results. Args: results: List of SentimentResult instances limit: Maximum number of keywords to return Returns: List of (keyword, count) tuples """ from collections import Counter all_keywords = [] for result in results: all_keywords.extend(result.keywords) keyword_counts = Counter(all_keywords) return keyword_counts.most_common(limit) def get_sentiment_distribution(results: list) -> dict: """ Get sentiment distribution from a list of results. Args: results: List of SentimentResult instances Returns: Dictionary with sentiment counts and percentages """ total = len(results) if total == 0: return { 'positive': {'count': 0, 'percentage': 0}, 'neutral': {'count': 0, 'percentage': 0}, 'negative': {'count': 0, 'percentage': 0}, } positive = sum(1 for r in results if r.sentiment == 'positive') neutral = sum(1 for r in results if r.sentiment == 'neutral') negative = sum(1 for r in results if r.sentiment == 'negative') return { 'positive': { 'count': positive, 'percentage': round((positive / total) * 100, 1) }, 'neutral': { 'count': neutral, 'percentage': round((neutral / total) * 100, 1) }, 'negative': { 'count': negative, 'percentage': round((negative / total) * 100, 1) }, }