import logging from celery import shared_task logger = logging.getLogger(__name__) POSITIVE_WORDS = { "excellent", "amazing", "wonderful", "great", "good", "fantastic", "outstanding", "helpful", "friendly", "professional", "kind", "caring", "clean", "comfortable", "satisfied", "impressed", "thankful", "grateful", "appreciate", "perfect", "superb", "brilliant", "exceptional", "positive", "recommend", "pleasant", "courteous", "efficient", "prompt", "compassionate", "skilled", "best", } NEGATIVE_WORDS = { "terrible", "awful", "horrible", "bad", "poor", "worst", "rude", "unprofessional", "dirty", "uncomfortable", "dissatisfied", "disappointed", "frustrated", "angry", "slow", "waiting", "waited", "delay", "delayed", "neglect", "neglected", "ignored", "complaint", "unacceptable", "never", "not", "no", "cannot", "painful", "careless", "arrogant", "unhelpful", "overpriced", "expensive", "messy", "noisy", "unsafe", "incompetent", "wrong", "mistake", "error", } def _analyze_text(text): if not text: return 0.0, 0, 0 words = text.lower().split() total = len(words) or 1 positive_hits = sum(1 for w in words if w in POSITIVE_WORDS) negative_hits = sum(1 for w in words if w in NEGATIVE_WORDS) score = (positive_hits - negative_hits) / total return max(-1.0, min(1.0, score)), positive_hits, negative_hits @shared_task def analyze_feedback_sentiment(feedback_id): from apps.feedback.models import Feedback, SentimentChoices try: feedback = Feedback.objects.get(id=feedback_id) except Feedback.DoesNotExist: logger.error(f"Feedback {feedback_id} not found") return {"status": "error", "reason": "not_found"} title_score, title_pos, title_neg = _analyze_text(feedback.title or "") message_score, msg_pos, msg_neg = _analyze_text(feedback.message or "") total_pos = title_pos + msg_pos total_neg = title_neg + msg_neg combined_score = (title_score + message_score) / 2 if (title_score or message_score) else 0.0 if combined_score > 0.05: sentiment = SentimentChoices.POSITIVE elif combined_score < -0.05: sentiment = SentimentChoices.NEGATIVE else: sentiment = SentimentChoices.NEUTRAL sentiment_score = round(combined_score, 4) feedback.sentiment = sentiment feedback.sentiment_score = sentiment_score feedback.save(update_fields=["sentiment", "sentiment_score"]) logger.info( f"Sentiment analysis for feedback {feedback_id}: " f"sentiment={sentiment}, score={sentiment_score} " f"(pos={total_pos}, neg={total_neg})" ) return { "status": "success", "feedback_id": str(feedback_id), "sentiment": sentiment, "sentiment_score": sentiment_score, "positive_hits": total_pos, "negative_hits": total_neg, } @shared_task def process_pending_sentiment_analysis(): from apps.feedback.models import Feedback pending = Feedback.objects.filter(sentiment="neutral", sentiment_score__isnull=True)[:100] dispatched = 0 for feedback in pending: analyze_feedback_sentiment.delay(str(feedback.id)) dispatched += 1 logger.info(f"Dispatched sentiment analysis for {dispatched} pending feedbacks") return {"dispatched": dispatched} @shared_task def analyze_suggestion_with_ai(feedback_id): from apps.feedback.models import Feedback, FeedbackCategory, FeedbackResponse from apps.core.ai_service import AIService try: feedback = Feedback.objects.get(id=feedback_id) except Feedback.DoesNotExist: logger.error(f"Feedback {feedback_id} not found for AI analysis") return {"status": "error", "reason": "not_found"} try: analysis = AIService.analyze_suggestion( message=feedback.message or "", title=feedback.title or None, ) category_map = { "clinical_care": FeedbackCategory.CLINICAL_CARE, "facility": FeedbackCategory.FACILITY, "staff_service": FeedbackCategory.STAFF_SERVICE, "communication": FeedbackCategory.COMMUNICATION, "technology": FeedbackCategory.TECHNOLOGY, "food_service": FeedbackCategory.FOOD_SERVICE, "appointment": FeedbackCategory.APPOINTMENT, "process_improvement": FeedbackCategory.OTHER, "other": FeedbackCategory.OTHER, } ai_category = category_map.get(analysis.get("category", "other"), FeedbackCategory.OTHER) ai_priority = analysis.get("priority", "medium") old_category = feedback.get_category_display() old_priority = feedback.get_priority_display() feedback.category = ai_category feedback.priority = ai_priority if not feedback.title or feedback.title.startswith(feedback.message[:80] if feedback.message else ""): ai_title = analysis.get("short_description_en", "") if ai_title: feedback.title = ai_title[:500] if not feedback.metadata: feedback.metadata = {} feedback.metadata["ai_analysis"] = { "short_description_en": analysis.get("short_description_en", ""), "short_description_ar": analysis.get("short_description_ar", ""), "category": analysis.get("category", "other"), "priority": ai_priority, "suggested_actions": analysis.get("suggested_actions", []), "suggested_action_en": analysis.get("suggested_action_en", ""), "suggested_action_ar": analysis.get("suggested_action_ar", ""), "reasoning_en": analysis.get("reasoning_en", ""), "reasoning_ar": analysis.get("reasoning_ar", ""), "old_category": old_category, "old_priority": old_priority, "analyzed_at": str(timezone.now().isoformat()), } feedback.save(update_fields=["title", "category", "priority", "metadata"]) FeedbackResponse.objects.create( feedback=feedback, response_type="note", message=f"AI analysis completed. Category: {feedback.get_category_display()}, Priority: {feedback.get_priority_display()}", created_by=None, is_internal=True, metadata={"ai_generated": True}, ) logger.info( f"Suggestion AI analysis completed for {feedback_id}: " f"category={analysis.get('category')}, priority={ai_priority}" ) return {"status": "success", "feedback_id": str(feedback_id)} except Exception as e: logger.error(f"Error analyzing suggestion {feedback_id}: {e}") return {"status": "error", "reason": str(e)}