diff --git a/EmotionDetection/__init__.py b/EmotionDetection/__init__.py new file mode 100644 index 000000000..0b7bf8661 --- /dev/null +++ b/EmotionDetection/__init__.py @@ -0,0 +1 @@ +from .emotion_detection import emotion_detector diff --git a/EmotionDetection/emotion_detection.py b/EmotionDetection/emotion_detection.py new file mode 100644 index 000000000..878d8de20 --- /dev/null +++ b/EmotionDetection/emotion_detection.py @@ -0,0 +1,48 @@ +import requests +import json + +def emotion_detector(text_to_analyze): + url = "https://sn-watson-emotion.labs.skills.network/v1/watson.runtime.nlp.v1/NlpService/EmotionPredict" + headers = {"grpc-metadata-mm-model-id": "emotion_aggregated-workflow_lang_en_stock"} + data = { "raw_document": { "text": text_to_analyze } } + + response = requests.post(url, json=data, headers=headers) + + if response.status_code == 400: + return { + 'anger': None, + 'disgust': None, + 'fear': None, + 'joy': None, + 'sadness': None, + 'dominant_emotion': None + } + + result = json.loads(response.text) + + emotions = result['emotionPredictions'][0]['emotion'] + anger = emotions['anger'] + disgust = emotions['disgust'] + fear = emotions['fear'] + joy = emotions['joy'] + sadness = emotions['sadness'] + + # Get the dominant emotion + emotion_scores = { + 'anger': anger, + 'disgust': disgust, + 'fear': fear, + 'joy': joy, + 'sadness': sadness + } + + dominant_emotion = max(emotion_scores, key=emotion_scores.get) + + return { + 'anger': anger, + 'disgust': disgust, + 'fear': fear, + 'joy': joy, + 'sadness': sadness, + 'dominant_emotion': dominant_emotion + } diff --git a/README.md b/README.md index 7f22b4324..01c057bb4 100644 --- a/README.md +++ b/README.md @@ -1 +1,231 @@ -# Repository for final project +# ๐Ÿง  AI-Based Emotion Detection Web Application + +A complete AI-powered web application built with Flask and IBM Watson NLP to detect **emotions** from customer feedback text. This project includes **data analysis**, **testing**, **error handling**, **static code analysis**, and **web deployment**. + +--- + +## ๐Ÿ“ Project Structure + +``` +final_project/ +โ”‚ +โ”œโ”€โ”€ EmotionDetection/ +โ”‚ โ”œโ”€โ”€ __init__.py +โ”‚ โ””โ”€โ”€ emotion_detection.py +โ”‚ +โ”œโ”€โ”€ static/ +โ”‚ โ””โ”€โ”€ mywebscript.js +โ”‚ +โ”œโ”€โ”€ templates/ +โ”‚ โ””โ”€โ”€ index.html +โ”‚ +โ”œโ”€โ”€ server.py +โ”œโ”€โ”€ test_emotion_detection.py +โ”œโ”€โ”€ README.md +โ””โ”€โ”€ [screenshots...] +``` + +--- + +## ๐Ÿงฉ Tasks Overview & Code + +### โœ… Task 1: Fork & Clone + +```bash +git clone https://github.com/YOUR_USERNAME/oaqjp-final-project-emb-ai.git final_project +cd final_project +``` + +--- + +### โœ… Task 2: Create Emotion Detection Module + +**File:** `emotion_detection.py` + +```python +import requests +import json + +def emotion_detector(text_to_analyze): + url = "https://sn-watson-emotion.labs.skills.network/v1/watson.runtime.nlp.v1/NlpService/EmotionPredict" + headers = {"grpc-metadata-mm-model-id": "emotion_aggregated-workflow_lang_en_stock"} + data = { "raw_document": { "text": text_to_analyze } } + response = requests.post(url, headers=headers, json=data) + if response.status_code == 400: + return { + 'anger': None, 'disgust': None, 'fear': None, + 'joy': None, 'sadness': None, 'dominant_emotion': None + } + response_json = json.loads(response.text) + emotions = response_json['emotionPredictions'][0]['emotion'] + dominant_emotion = max(emotions, key=emotions.get) + return { + 'anger': emotions['anger'], + 'disgust': emotions['disgust'], + 'fear': emotions['fear'], + 'joy': emotions['joy'], + 'sadness': emotions['sadness'], + 'dominant_emotion': dominant_emotion + } +``` + +--- + +### โœ… Task 3: Format Output + +Already included above in `emotion_detector()` function. + +--- + +### โœ… Task 4: Package as Module + +**Folder:** `EmotionDetection/` +**File:** `__init__.py` + +```python +from .emotion_detection import emotion_detector +``` + +--- + +### โœ… Task 5: Unit Testing + +**File:** `test_emotion_detection.py` + +```python +import unittest +from EmotionDetection.emotion_detection import emotion_detector + +class TestEmotionDetection(unittest.TestCase): + def test_joy(self): + self.assertEqual(emotion_detector("I am glad this happened")['dominant_emotion'], "joy") + def test_anger(self): + self.assertEqual(emotion_detector("I am really mad about this")['dominant_emotion'], "anger") + def test_disgust(self): + self.assertEqual(emotion_detector("I feel disgusted just hearing about this")['dominant_emotion'], "disgust") + def test_sadness(self): + self.assertEqual(emotion_detector("I am so sad about this")['dominant_emotion'], "sadness") + def test_fear(self): + self.assertEqual(emotion_detector("I am really afraid that this will happen")['dominant_emotion'], "fear") + +if __name__ == '__main__': + unittest.main() +``` + +Run the tests: + +```bash +python3 test_emotion_detection.py +``` + +--- + +### โœ… Task 6: Web Deployment with Flask + +**File:** `server.py` + +```python +from flask import Flask, request, render_template +from EmotionDetection.emotion_detection import emotion_detector + +app = Flask(__name__) + +@app.route('/emotionDetector') +def emotion_detector_route(): + text = request.args.get('textToAnalyze') + result = emotion_detector(text) + if result['dominant_emotion'] is None: + return "Invalid text! Please try again!" + response = (f"For the given statement, the system response is " + f"'anger': {result['anger']}, 'disgust': {result['disgust']}, " + f"'fear': {result['fear']}, 'joy': {result['joy']} and " + f"'sadness': {result['sadness']}. The dominant emotion is " + f"{result['dominant_emotion']}.") + return response + +if __name__ == '__main__': + app.run(debug=True) +``` + +Run the app: + +```bash +python3 server.py +``` + +Access at: [http://localhost:5000](http://localhost:5000) + +--- + +### โœ… Task 7: Error Handling + +Handled in `emotion_detector()` (via `status_code == 400`) and in `server.py` (`dominant_emotion == None`). + +--- + +### โœ… Task 8: Static Code Analysis + +Run Pylint: + +```bash +pylint server.py +``` + +Ensure functions have proper **docstrings** to get a 10/10 score. + +--- + +## ๐Ÿ’ก Sample Input/Output + +**Input Text:** "I love my life" + +**Formatted Output:** + +```json +{ + "anger": 0.0062, + "disgust": 0.0025, + "fear": 0.0092, + "joy": 0.9680, + "sadness": 0.0497, + "dominant_emotion": "joy" +} +``` + +**Response Message:** +"For the given statement, the system response is 'anger': 0.0062, 'disgust': 0.0025, 'fear': 0.0092, 'joy': 0.9680 and 'sadness': 0.0497. The dominant emotion is joy." + +--- + +## ๐Ÿ“ธ Screenshots + +All screenshots from each task (e.g., `1_folder_structure.png`, `2a_emotion_detection.png`, `6b_deployment_test.png`) are included in the root project directory. + +--- + +## ๐Ÿงช Technologies Used + +- Python 3.11 +- Flask (Web framework) +- Watson NLP API (Emotion Detection) +- Requests (HTTP client) +- unittest (Testing) +- pylint (Static Code Analysis) + +--- + +## ๐Ÿ‘จโ€๐Ÿ’ป Author + +**Souvik Mandal** +IBM AI Engineering Final Project +GitHub: [Souvik Mandal](https://github.com/AP19110010485) + +--- + +## ๐Ÿ Status + +โœ… All 8 tasks completed +โœ… Fully functional and tested +โœ… 10/10 code quality + +๐ŸŽ‰ Thank you for visiting the project! diff --git a/Screenshots/1_folder_structure.png b/Screenshots/1_folder_structure.png new file mode 100644 index 000000000..61aafa954 Binary files /dev/null and b/Screenshots/1_folder_structure.png differ diff --git a/Screenshots/2a_emotion_detection.png b/Screenshots/2a_emotion_detection.png new file mode 100644 index 000000000..c51089ff9 Binary files /dev/null and b/Screenshots/2a_emotion_detection.png differ diff --git a/Screenshots/2b_application_creation.png b/Screenshots/2b_application_creation.png new file mode 100644 index 000000000..bf55a8369 Binary files /dev/null and b/Screenshots/2b_application_creation.png differ diff --git a/Screenshots/3a_output_formatting.png b/Screenshots/3a_output_formatting.png new file mode 100644 index 000000000..566866423 Binary files /dev/null and b/Screenshots/3a_output_formatting.png differ diff --git a/Screenshots/3b_formatted_output_test.png b/Screenshots/3b_formatted_output_test.png new file mode 100644 index 000000000..c8700dee7 Binary files /dev/null and b/Screenshots/3b_formatted_output_test.png differ diff --git a/Screenshots/4a_packaging.png b/Screenshots/4a_packaging.png new file mode 100644 index 000000000..46b7614fe Binary files /dev/null and b/Screenshots/4a_packaging.png differ diff --git a/Screenshots/4b_packaging_test.png b/Screenshots/4b_packaging_test.png new file mode 100644 index 000000000..0c7a33c58 Binary files /dev/null and b/Screenshots/4b_packaging_test.png differ diff --git a/Screenshots/5a_unit_testing.png b/Screenshots/5a_unit_testing.png new file mode 100644 index 000000000..7331692a4 Binary files /dev/null and b/Screenshots/5a_unit_testing.png differ diff --git a/Screenshots/5b_unit_testing_result.png b/Screenshots/5b_unit_testing_result.png new file mode 100644 index 000000000..d2926d323 Binary files /dev/null and b/Screenshots/5b_unit_testing_result.png differ diff --git a/Screenshots/6a_server.png b/Screenshots/6a_server.png new file mode 100644 index 000000000..03207b6b9 Binary files /dev/null and b/Screenshots/6a_server.png differ diff --git a/Screenshots/6b_deployment_test.png b/Screenshots/6b_deployment_test.png new file mode 100644 index 000000000..37f5d7e61 Binary files /dev/null and b/Screenshots/6b_deployment_test.png differ diff --git a/Screenshots/7a_error_handling_function.png b/Screenshots/7a_error_handling_function.png new file mode 100644 index 000000000..c9d807a87 Binary files /dev/null and b/Screenshots/7a_error_handling_function.png differ diff --git a/Screenshots/7b_error_handling_server.png b/Screenshots/7b_error_handling_server.png new file mode 100644 index 000000000..1391a11aa Binary files /dev/null and b/Screenshots/7b_error_handling_server.png differ diff --git a/Screenshots/7c_error_handling_interface.png b/Screenshots/7c_error_handling_interface.png new file mode 100644 index 000000000..94b756976 Binary files /dev/null and b/Screenshots/7c_error_handling_interface.png differ diff --git a/Screenshots/8a_server_modified.png b/Screenshots/8a_server_modified.png new file mode 100644 index 000000000..7f3b0ebe6 Binary files /dev/null and b/Screenshots/8a_server_modified.png differ diff --git a/Screenshots/8b_static_code_analysis.png b/Screenshots/8b_static_code_analysis.png new file mode 100644 index 000000000..f10ad384c Binary files /dev/null and b/Screenshots/8b_static_code_analysis.png differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..b161a168c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask==2.3.2 +requests==2.31.0 +pylint==3.0.2 diff --git a/server.py b/server.py new file mode 100644 index 000000000..ed4fe96b3 --- /dev/null +++ b/server.py @@ -0,0 +1,33 @@ +"""Flask server for Emotion Detection App.""" + +from flask import Flask, request, render_template +from EmotionDetection.emotion_detection import emotion_detector + +app = Flask(__name__) + +@app.route("/") +def render_index_page(): + """Renders the main page of the application.""" + return render_template("index.html") + +@app.route("/emotionDetector") +def analyze_emotion(): + """Handles emotion analysis request and returns formatted response.""" + text_to_analyze = request.args.get("textToAnalyze") + response = emotion_detector(text_to_analyze) + + if response["dominant_emotion"] is None: + return "Invalid text! Please try again!" + + return ( + f"For the given statement, the system response is " + f"'anger': {response['anger']}, " + f"'disgust': {response['disgust']}, " + f"'fear': {response['fear']}, " + f"'joy': {response['joy']} and " + f"'sadness': {response['sadness']}. " + f"The dominant emotion is {response['dominant_emotion']}." + ) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000) diff --git a/test_emotion_detection.py b/test_emotion_detection.py new file mode 100644 index 000000000..ba09b92d6 --- /dev/null +++ b/test_emotion_detection.py @@ -0,0 +1,26 @@ +import unittest +from EmotionDetection import emotion_detector + +class TestEmotionDetector(unittest.TestCase): + def test_joy(self): + result = emotion_detector("I am glad this happened") + self.assertEqual(result['dominant_emotion'], 'joy') + + def test_anger(self): + result = emotion_detector("I am really mad about this") + self.assertEqual(result['dominant_emotion'], 'anger') + + def test_disgust(self): + result = emotion_detector("I feel disgusted just hearing about this") + self.assertEqual(result['dominant_emotion'], 'disgust') + + def test_sadness(self): + result = emotion_detector("I am so sad about this") + self.assertEqual(result['dominant_emotion'], 'sadness') + + def test_fear(self): + result = emotion_detector("I am really afraid that this will happen") + self.assertEqual(result['dominant_emotion'], 'fear') + +if __name__ == '__main__': + unittest.main()