Skip to content

Commit

Permalink
Merge pull request #36 from software-students-fall2023/rqn
Browse files Browse the repository at this point in the history
Rqn
  • Loading branch information
Ryan-Horng authored Dec 4, 2023
2 parents 3b3d206 + 4def61b commit abb0229
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 82 deletions.
Empty file.
58 changes: 26 additions & 32 deletions machine-learning-client/machine_learning_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
to text transcription and analysis of audio transcription
"""

import sys
import pyaudio
import pymongo
import pyaudio

import os
import pyaudio


from pymongo import MongoClient
import speech_recognition as sr
from flask_cors import CORS
from flask import Flask, request, jsonify, send_from_directory
from flask import Flask, request, jsonify
from pydub import AudioSegment

app = Flask(__name__)
Expand All @@ -23,6 +23,7 @@
db = client["ml_databse"]
collection = db["transcription"]


app.config["UPLOAD_FOLDER"] = "uploads"
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
Expand All @@ -39,7 +40,7 @@ def add_transcription_mongo(transcription, grade_report):
result = collection.insert_one(data)
print(f"Transcription saved to MongoDB with ID: {result.inserted_id}")

except Exception as e:
except Exception as e: # pylint: disable=broad-exception-caught
print(f"Error saving transcription to MongoDB: {e}")


Expand All @@ -61,22 +62,21 @@ def audio_to_text(audio_file_string):
return transcription


def grade_transcription(transcription_text):
def grade_transcription(
transcription_text,
): # pylint: disable=too-many-branches,too-many-statements
"""
Function to generate a page giving a break down and grade of an audio transcript someone had just recorded
Function to generate a page giving a break down and grade of an audio transcript someone had just recorded # pylint: disable=line-too-long
"""

grade = 0
filler_words = 0
buzz_words = 0

"""filler_words += my_transcript.find()"""
filler_words += transcription_text.lower().count("uh")
filler_words += transcription_text.lower().count("um")
filler_words += transcription_text.lower().count("ah")
filler_words += transcription_text.lower().count("like")
filler_words += transcription_text.lower().count("er")

buzz_words += transcription_text.lower().count("education")
buzz_words += transcription_text.lower().count("major")
buzz_words += transcription_text.lower().count("college")
Expand All @@ -91,59 +91,53 @@ def grade_transcription(transcription_text):
buzz_words += transcription_text.lower().count("develop")
buzz_words += transcription_text.lower().count("fulfill")
buzz_words += transcription_text.lower().count("refine")

print("this value of filler word: ", filler_words)
print("this value of buzz word: ", buzz_words)

fw_breakdown = ""
bz_breakdown = ""
grade_analysis = ""

if buzz_words == 0:
bz_breakdown = "You did not use any buzz words. When answering this question, try to talk about your education, work experiences, major, or any jobs you had."
bz_breakdown = "You did not use any buzz words. When answering this question, try to talk about your education, work experiences, major, or any jobs you had." # pylint: disable=line-too-long
elif 0 < buzz_words <= 5:
bz_breakdown = f"You used {buzz_words} buzz words. A decent answer, but you can improve. Try to find more things to say about yourself professionally."
bz_breakdown = f"You used {buzz_words} buzz words. A decent answer, but you can improve. Try to find more things to say about yourself professionally." # pylint: disable=line-too-long
else:
bz_breakdown = f"You used {buzz_words} buzz words. The topics of your answer are acceptable. Make sure to keep in mind your wording and topics."

bz_breakdown = f"You used {buzz_words} buzz words. The topics of your answer are acceptable. Make sure to keep in mind your wording and topics." # pylint: disable=line-too-long
if filler_words == 0:
fw_breakdown = "You did not use any filler words. Or there was a problem with the audio transcript. Either way, remember to talk confidently and at a good pace."
fw_breakdown = "You did not use any filler words. Or there was a problem with the audio transcript. Either way, remember to talk confidently and at a good pace." # pylint: disable=line-too-long
elif 0 < filler_words <= 5:
fw_breakdown = f"You used {filler_words} filler words. From here, you just need to practice your answer until it comes out naturally. Make sure to keep relaxed."
fw_breakdown = f"You used {filler_words} filler words. From here, you just need to practice your answer until it comes out naturally. Make sure to keep relaxed." # pylint: disable=line-too-long
else:
fw_breakdown = f"You used {filler_words} filler words. When giving your answer, try speaking slower and more clearly. Don't confuse yourself by thinking too far ahead."

fw_breakdown = f"You used {filler_words} filler words. When giving your answer, try speaking slower and more clearly. Don't confuse yourself by thinking too far ahead." # pylint: disable=line-too-long
grade = (buzz_words * 3) - filler_words
print(grade)
if grade > 20:
grade = 20
elif grade < 0:
grade = 0
if grade == 0:
grade_analysis = f"Your grade is {grade}/20. You should rethink your answer to be more focused and clearly thought out."
grade_analysis = f"Your grade is {grade}/20. You should rethink your answer to be more focused and clearly thought out." # pylint: disable=line-too-long
elif 0 < grade <= 5:
grade_analysis = f"Your grade is {grade}/20. Refine your answer by reflecting on your education, work experience, and relevant info."
grade_analysis = f"Your grade is {grade}/20. Refine your answer by reflecting on your education, work experience, and relevant info." # pylint: disable=line-too-long
elif 5 < grade <= 10:
grade_analysis = f"Your grade is {grade}/20. You're answer fulfills some of the necessary buzz words. If you have any more relevant data or ways to improve your speech, do so."
grade_analysis = f"Your grade is {grade}/20. You're answer fulfills some of the necessary buzz words. If you have any more relevant data or ways to improve your speech, do so." # pylint: disable=line-too-long
elif 10 < grade <= 15:
grade_analysis = f"Your grade is {grade}/20. You gave a very good response. Everything beyond this is fine tuning your response to the interviewer."
grade_analysis = f"Your grade is {grade}/20. You gave a very good response. Everything beyond this is fine tuning your response to the interviewer." # pylint: disable=line-too-long
else:
grade_analysis = f"Your grade is {grade}/20. This should be your go to answer, but it all depends on the interviewer. This answer should could out naturally and be your basis."

grade_analysis = f"Your grade is {grade}/20. This should be your go to answer, but it all depends on the interviewer. This answer should could out naturally and be your basis." # pylint: disable=line-too-long
grade_report = {
"filler_words": fw_breakdown,
"buzz_words": bz_breakdown,
"grade_analysis": grade_analysis,
}

print(fw_breakdown)
print(bz_breakdown)
print(grade_analysis)
return grade_report


@app.route("/analyzeAudio", methods=["POST"])
def analyze_Audios():
def analyze_Audios(): # pylint: disable=invalid-name
"""
Endpoint to receive and analyze audio file
"""
Expand Down Expand Up @@ -185,16 +179,16 @@ def analyze_Audios():
transcription = audio_to_text(true_audio_path_str)
print(transcription)
print("calling grade to text method")
gradedTranscription = grade_transcription(transcription)
add_transcription_mongo(transcription, gradedTranscription)
graded_transcription = grade_transcription(transcription)
add_transcription_mongo(transcription, graded_transcription)

print("we did it baby")
return (
jsonify({"status": "success", "message": "Audio analysis completed"}),
200,
)

except Exception as e:
except Exception as e: # pylint: disable=broad-exception-caught
return (
jsonify({"status": "error", "message": f"An error occurred: {str(e)}"}),
500,
Expand Down
123 changes: 76 additions & 47 deletions machine-learning-client/tests/ml_tests.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,92 @@
"""
tests for machine-learning-client
"""
import machine_learning_client as ML
import os
import machine_learning_client as ML # pylint: disable=import-error
import pytest
from flask import Flask
from flask_cors import CORS
from flask import Flask, request, jsonify
from flask.testing import FlaskClient
from flask_cors import CORS
from pytest_flask import fixtures
from machine_learning_client import audio_to_text, grade_transcription, add_transcription_mongo


# Mocking dependencies and functions
@pytest.fixture
def mock_dependencies(monkeypatch):
monkeypatch.setattr("machine_learning_client.audio_to_text", lambda *args, **kwargs: "mocked_transcription")
monkeypatch.setattr("machine_learning_client.grade_transcription", lambda *args, **kwargs: "mocked_grade")
monkeypatch.setattr("machine_learning_client.add_transcription_mongo", lambda *args, **kwargs: None)
"""Setup Mock"""
monkeypatch.setattr(
"machine_learning_client.audio_to_text",
lambda *args, **kwargs: "mocked_transcription",
)
monkeypatch.setattr(
"machine_learning_client.grade_transcription",
lambda *args, **kwargs: "mocked_grade",
)
monkeypatch.setattr(
"machine_learning_client.add_transcription_mongo", lambda *args, **kwargs: None
)


# Create a fixture for the Flask application with CORS enabled
@pytest.fixture
def client():
with ML.app.test_client() as client:
"""Create a fixture for the Flask application with CORS enabled"""
with ML.app.test_client() as client: # pylint: disable=redefined-outer-name
yield client


# Test the route handler with a valid audio file
def test_analyze_audios_valid_audio(client):
def test_analyze_audios_valid_audio(client): # pylint: disable=redefined-outer-name
"""Test the route handler with a valid audio file"""
audio_file_path = os.path.join(os.path.dirname(__file__), "test_audio.webm")
print(audio_file_path)
with open(audio_file_path, "rb") as audio_file:
response = client.post("/analyzeAudio", data={"audio": (audio_file, "test_audio.webm")})
response = client.post(
"/analyzeAudio", data={"audio": (audio_file, "test_audio.webm")}
)

assert response.status_code == 200
assert response.json == {"status": "success", "message": "Audio analysis completed"}


# Test the route handler without including an audio file
def test_analyze_audios_no_audio_file(client):
def test_analyze_audios_no_audio_file(client): # pylint: disable=redefined-outer-name
"""Test the route handler without including an audio file"""
response = client.post("/analyzeAudio")

assert response.status_code == 400
assert response.json == {"status": "error", "message": "No audio file provided"}


# Test the route handler with an exception during audio processing
def test_analyze_audios_exception(client, monkeypatch):
monkeypatch.setattr("machine_learning_client.audio_to_text", lambda *args, **kwargs: Exception("Mocked exception"))
def test_analyze_audios_exception(
client, monkeypatch
): # pylint: disable=redefined-outer-name
"""Test the route handler with an exception during audio processing"""
monkeypatch.setattr(
"machine_learning_client.audio_to_text",
lambda *args, **kwargs: Exception("Mocked exception"),
)

audio_file_path = os.path.join(os.path.dirname(__file__), "test_audio.webm")

with open(audio_file_path, "rb") as audio_file:
response = client.post("/analyzeAudio", data={"audio": (audio_file, "test_audio.webm")})
response = client.post(
"/analyzeAudio", data={"audio": (audio_file, "test_audio.webm")}
)

assert response.status_code == 500
assert response.json["status"] == "error"

# Asserting that the error message starts with "An error occurred:"
assert response.json["message"].startswith("An error occurred:")


class Tests:
"""Tests class for ml_tests.py"""

def test_audio_text_conversion(self):
"""testing audio_to_text function in ML with valid audiofile as input"""
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
tests_path = "tests"
file_name = "richard.aiff"
file_path = os.path.join(PROJECT_ROOT, tests_path, file_name)
file_path = os.path.join(project_root, tests_path, file_name)
print(file_path)
actual = ML.audio_to_text(file_path)
assert isinstance(
Expand All @@ -72,45 +95,51 @@ def test_audio_text_conversion(self):
assert (
actual.lower() == "my name is richard"
), f"Expected the converted audio to be 'my name is richard'. Instead it returned {actual}"




def test_grade_transcription_good(self):
"""testing grade_transcription function in ML with a recording of all filler && buzz words"""
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
"""testing grade_transcription function in ML with a recording of all filler && buzz words""" # pylint: disable=line-too-long
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
tests_path = "tests"
file_name = "filler.aiff"
file_path = os.path.join(PROJECT_ROOT, tests_path, file_name)
file_path = os.path.join(project_root, tests_path, file_name)
actual = ML.grade_transcription(ML.audio_to_text(file_path))
assert isinstance(actual, dict), f"Expected grade_transcription to return a dict. Instead it returned a {actual}"
assert (actual == {"filler_words": "You used 7 filler words. When giving your answer, try speaking slower and more clearly. Don't confuse yourself by thinking too far ahead.",
"buzz_words": "You used 14 buzz words. The topics of your answer are acceptable. Make sure to ",
"grade_analysis": "Your grade is 20/20. This should be your go to answer, but it all depends on the interviewer. This answer should could out naturally and be your basis."}
), f"Expected a 20 grade report. Instead it returned {actual}"

assert isinstance(
actual, dict
), f"Expected grade_transcription to return a dict. Instead it returned a {actual}"
assert actual == {
"filler_words": "You used 7 filler words. When giving your answer, try speaking slower and more clearly. Don't confuse yourself by thinking too far ahead.", # pylint: disable=line-too-long
"buzz_words": "You used 14 buzz words. The topics of your answer are acceptable. Make sure to keep in mind your wording and topics.", # pylint: disable=line-too-long
"grade_analysis": "Your grade is 20/20. This should be your go to answer, but it all depends on the interviewer. This answer should could out naturally and be your basis.", # pylint: disable=line-too-long
}, f"Expected a 20 grade report. Instead it returned {actual}"

def test_grade_transcription_gt5(self):
"""testing grade_transcription function in ML with a recording of many filler/buzz words"""
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
tests_path = "tests"
file_name = "gt5.aiff"
file_path = os.path.join(PROJECT_ROOT, tests_path, file_name)
file_path = os.path.join(project_root, tests_path, file_name)
actual = ML.grade_transcription(ML.audio_to_text(file_path))
assert isinstance(actual, dict), f"Expected grade_transcription to return a dict. Instead it returned a {actual}"
assert (actual == {"filler_words": "You did not use any filler words. Or there was a problem with the audio transcript. Either way, remember to talk confidently and at a good pace.",
"buzz_words": "You did not use any buzz words. When answering this question, try to talk about your education, work experiences, major, or any jobs you had.",
"grade_analysis": "Your grade is 0/20. You should rethink your answer to be more focused and clearly thought out."}
), f"Expected a 0 grade report. Instead it returned {actual}"

assert isinstance(
actual, dict
), f"Expected grade_transcription to return a dict. Instead it returned a {actual}"
assert actual == {
"filler_words": "You did not use any filler words. Or there was a problem with the audio transcript. Either way, remember to talk confidently and at a good pace.", # pylint: disable=line-too-long
"buzz_words": "You did not use any buzz words. When answering this question, try to talk about your education, work experiences, major, or any jobs you had.", # pylint: disable=line-too-long
"grade_analysis": "Your grade is 0/20. You should rethink your answer to be more focused and clearly thought out.", # pylint: disable=line-too-long
}, f"Expected a 0 grade report. Instead it returned {actual}"

def test_grade_transcription_5(self):
"""testing grade_transcription function in ML with a recording of 5 filler/buzz"""
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
tests_path = "tests"
file_name = "5.aiff"
file_path = os.path.join(PROJECT_ROOT, tests_path, file_name)
file_path = os.path.join(project_root, tests_path, file_name)
actual = ML.grade_transcription(ML.audio_to_text(file_path))
assert isinstance(actual, dict), f"Expected grade_transcription to return a dict. Instead it returned a {actual}"
assert (actual == {"filler_words": "You used 16 filler words. When giving your answer, try speaking slower and more clearly. Don't confuse yourself by thinking too far ahead.",
"buzz_words": "You did not use any buzz words. When answering this question, try to talk about your education, work experiences, major, or any jobs you had.",
"grade_analysis": "Your grade is 0/20. You should rethink your answer to be more focused and clearly thought out."}
), f"Expected a 0 grade report. Instead it returned {actual}"
assert isinstance(
actual, dict
), f"Expected grade_transcription to return a dict. Instead it returned a {actual}"
assert actual == {
"filler_words": "You used 16 filler words. When giving your answer, try speaking slower and more clearly. Don't confuse yourself by thinking too far ahead.", # pylint: disable=line-too-long
"buzz_words": "You did not use any buzz words. When answering this question, try to talk about your education, work experiences, major, or any jobs you had.", # pylint: disable=line-too-long
"grade_analysis": "Your grade is 0/20. You should rethink your answer to be more focused and clearly thought out.", # pylint: disable=line-too-long
}, f"Expected a 0 grade report. Instead it returned {actual}"
4 changes: 1 addition & 3 deletions web-app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ def analyze_data():
result = response.json()
return jsonify(result)
return (
jsonify(
{"error": "Failed to send and process audio. Please try again."}
),
jsonify({"error": "Failed to send and process audio. Please try again."}),
500,
)

Expand Down

0 comments on commit abb0229

Please sign in to comment.