162 lines
3.5 KiB
Python
162 lines
3.5 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}
|