From 7861e2c54e97b27afc346e52d2a775b65a9e7123 Mon Sep 17 00:00:00 2001 From: Rohit Date: Wed, 30 Oct 2024 11:56:51 -0400 Subject: [PATCH 1/3] Add sentiment class --- codecov/sentiment.py | 34 ++++++++++++++++++++++++++++++++++ requirements.in | 2 ++ requirements.txt | 20 ++++++++++++++++++-- 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 codecov/sentiment.py diff --git a/codecov/sentiment.py b/codecov/sentiment.py new file mode 100644 index 0000000000..951516dd28 --- /dev/null +++ b/codecov/sentiment.py @@ -0,0 +1,34 @@ +from textblob import TextBlob + +class SentimentAnalyzer: + def __init__(self): + pass + + def get_sentiment(self, sentence: str) -> str: + polarity = self._get_polarity(sentence) + if polarity > 0: + return "positive" + elif polarity < 0: + return "negative" + else: + return "neutral" + + def get_polarity_score(self, sentence: str) -> float: + return self._get_polarity(sentence) + + def get_subjectivity_score(self, sentence: str) -> float: + return self._get_subjectivity(sentence) + + def is_neutral(self, sentence: str) -> bool: + return self.get_sentiment(sentence) == "neutral" + + def is_positive(self, sentence: str) -> bool: + return self.get_sentiment(sentence) == "positive" + + def _get_polarity(self, sentence: str) -> float: + analysis = TextBlob(sentence) + return analysis.sentiment.polarity + + def _get_subjectivity(self, sentence: str) -> float: + analysis = TextBlob(sentence) + return analysis.sentiment.subjectivity diff --git a/requirements.in b/requirements.in index 722646d649..11a67c92e9 100644 --- a/requirements.in +++ b/requirements.in @@ -33,6 +33,8 @@ pre-commit psycopg2 PyJWT pydantic +nltk==3.8.1 +textblob==0.17.1 pytest>=7.2.0 pytest-cov pytest-django diff --git a/requirements.txt b/requirements.txt index 0ab24f111d..f4fb164661 100644 --- a/requirements.txt +++ b/requirements.txt @@ -83,6 +83,7 @@ click==8.1.7 # click-didyoumean # click-plugins # click-repl + # nltk click-didyoumean==0.3.0 # via celery click-plugins==1.1.1 @@ -177,6 +178,7 @@ freezegun==1.1.0 # via -r requirements.in google-api-core[grpc]==2.11.1 # via + # google-api-core # google-cloud-core # google-cloud-pubsub # google-cloud-storage @@ -249,6 +251,8 @@ jmespath==0.10.0 # via # boto3 # botocore +joblib==1.4.2 + # via nltk jsonschema==4.14.0 # via drf-spectacular kombu==5.3.6 @@ -263,6 +267,10 @@ monotonic==1.5 # via analytics-python multidict==4.7.6 # via yarl +nltk==3.8.1 + # via + # -r requirements.in + # textblob nodeenv==1.5.0 # via pre-commit oauth2==1.9.0.post1 @@ -397,7 +405,9 @@ redis==4.4.4 # python-redis-lock # shared regex==2023.12.25 - # via -r requirements.in + # via + # -r requirements.in + # nltk requests==2.32.3 # via # -r requirements.in @@ -408,7 +418,9 @@ requests==2.32.3 # shared # stripe rfc3986[idna2008]==1.4.0 - # via httpx + # via + # httpx + # rfc3986 rsa==4.7.2 # via google-auth s3transfer==0.5.0 @@ -455,10 +467,14 @@ stripe==9.6.0 # via -r requirements.in text-unidecode==1.3 # via faker +textblob==0.17.1 + # via -r requirements.in tlslite-ng==0.8.0b1 # via shared toml==0.10.2 # via pre-commit +tqdm==4.66.6 + # via nltk typing==3.7.4.3 # via shared typing-extensions==4.6.2 From 67db86c772b91963455b000eab5a85221f2b7ff4 Mon Sep 17 00:00:00 2001 From: Rohit Date: Wed, 30 Oct 2024 11:57:02 -0400 Subject: [PATCH 2/3] Lint --- codecov/sentiment.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/codecov/sentiment.py b/codecov/sentiment.py index 951516dd28..1af2981a30 100644 --- a/codecov/sentiment.py +++ b/codecov/sentiment.py @@ -1,9 +1,10 @@ from textblob import TextBlob + class SentimentAnalyzer: def __init__(self): pass - + def get_sentiment(self, sentence: str) -> str: polarity = self._get_polarity(sentence) if polarity > 0: @@ -12,23 +13,23 @@ def get_sentiment(self, sentence: str) -> str: return "negative" else: return "neutral" - + def get_polarity_score(self, sentence: str) -> float: return self._get_polarity(sentence) - + def get_subjectivity_score(self, sentence: str) -> float: return self._get_subjectivity(sentence) - + def is_neutral(self, sentence: str) -> bool: return self.get_sentiment(sentence) == "neutral" - + def is_positive(self, sentence: str) -> bool: return self.get_sentiment(sentence) == "positive" def _get_polarity(self, sentence: str) -> float: analysis = TextBlob(sentence) return analysis.sentiment.polarity - + def _get_subjectivity(self, sentence: str) -> float: analysis = TextBlob(sentence) return analysis.sentiment.subjectivity From fa4ca76eab9065ce1e6bdd893cbb3951a0d5df76 Mon Sep 17 00:00:00 2001 From: "sentry-autofix[bot]" <157164994+sentry-autofix[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:24:41 -0400 Subject: [PATCH 3/3] Add Tests for PR#945 (#946) Co-authored-by: sentry-autofix[bot] <157164994+sentry-autofix[bot]@users.noreply.github.com> Co-authored-by: Rohit --- codecov/tests/test_sentiment.py | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 codecov/tests/test_sentiment.py diff --git a/codecov/tests/test_sentiment.py b/codecov/tests/test_sentiment.py new file mode 100644 index 0000000000..e56e4ae37c --- /dev/null +++ b/codecov/tests/test_sentiment.py @@ -0,0 +1,76 @@ +from unittest.mock import patch + +import pytest + +from codecov.sentiment import SentimentAnalyzer + + +@pytest.fixture +def sentiment_analyzer(): + return SentimentAnalyzer() + + +@patch("codecov.sentiment.TextBlob") +def test_get_sentiment_positive(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.5 + assert sentiment_analyzer.get_sentiment("Great job!") == "positive" + + +@patch("codecov.sentiment.TextBlob") +def test_get_sentiment_negative(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = -0.5 + assert sentiment_analyzer.get_sentiment("Terrible experience.") == "negative" + + +@patch("codecov.sentiment.TextBlob") +def test_get_sentiment_neutral(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.0 + assert sentiment_analyzer.get_sentiment("This is a neutral statement.") == "neutral" + + +@patch("codecov.sentiment.TextBlob") +def test_get_polarity_score(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.75 + assert sentiment_analyzer.get_polarity_score("Excellent work!") == 0.75 + + +@patch("codecov.sentiment.TextBlob") +def test_get_subjectivity_score(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.subjectivity = 0.6 + assert sentiment_analyzer.get_subjectivity_score("I think this is amazing!") == 0.6 + + +@patch("codecov.sentiment.TextBlob") +def test_is_neutral_true(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.0 + assert sentiment_analyzer.is_neutral("This is a fact.") == True + + +@patch("codecov.sentiment.TextBlob") +def test_is_neutral_false(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.5 + assert sentiment_analyzer.is_neutral("This is great!") == False + + +@patch("codecov.sentiment.TextBlob") +def test_is_positive_true(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.5 + assert sentiment_analyzer.is_positive("This is wonderful!") == True + + +@patch("codecov.sentiment.TextBlob") +def test_is_positive_false(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = -0.5 + assert sentiment_analyzer.is_positive("This is awful.") == False + + +@patch("codecov.sentiment.TextBlob") +def test_private_get_polarity(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.polarity = 0.8 + assert sentiment_analyzer._get_polarity("Amazing!") == 0.8 + + +@patch("codecov.sentiment.TextBlob") +def test_private_get_subjectivity(mock_textblob, sentiment_analyzer): + mock_textblob.return_value.sentiment.subjectivity = 0.9 + assert sentiment_analyzer._get_subjectivity("I absolutely love this!") == 0.9