HH/apps/feedback/tasks.py
ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

244 lines
6.9 KiB
Python

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)}