from fuzzywuzzy import fuzz from mathtext_fastapi.logging import prepare_message_data_for_logging from mathtext.sentiment import sentiment from mathtext.text2int import text2int from mathtext_fastapi.intent_classification import create_intent_classification_model, retrieve_intent_classification_model, predict_message_intent import re def build_nlu_response_object(type, data, confidence): """ Turns nlu results into an object to send back to Turn.io Inputs - type: str - the type of nlu run (integer or sentiment-analysis) - data: str/int - the student message - confidence: - the nlu confidence score (sentiment) or '' (integer) >>> build_nlu_response_object('integer', 8, 0) {'type': 'integer', 'data': 8, 'confidence': 0} >>> build_nlu_response_object('sentiment', 'POSITIVE', 0.99) {'type': 'sentiment', 'data': 'POSITIVE', 'confidence': 0.99} """ return {'type': type, 'data': data, 'confidence': confidence} # def test_for_float_or_int(message_data, message_text): # nlu_response = {} # if type(message_text) == int or type(message_text) == float: # nlu_response = build_nlu_response_object('integer', message_text, '') # prepare_message_data_for_logging(message_data, nlu_response) # return nlu_response def test_for_number_sequence(message_text_arr, message_data, message_text): """ Determines if the student's message is a sequence of numbers >>> test_for_number_sequence(['1','2','3'], {"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "I am tired", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"}, '1, 2, 3') {'type': 'integer', 'data': '1,2,3', 'confidence': 0} >>> test_for_number_sequence(['a','b','c'], {"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "I am tired", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"}, 'a, b, c') {} """ nlu_response = {} if all(ele.isdigit() for ele in message_text_arr): nlu_response = build_nlu_response_object( 'integer', ','.join(message_text_arr), 0 ) prepare_message_data_for_logging(message_data, nlu_response) return nlu_response def run_text2int_on_each_list_item(message_text_arr): """ Attempts to convert each list item to an integer Input - message_text_arr: list - a set of text extracted from the student message Output - student_response_arr: list - a set of integers (32202 for error code) >>> run_text2int_on_each_list_item(['1','2','3']) [1, 2, 3] """ student_response_arr = [] for student_response in message_text_arr: int_api_resp = text2int(student_response.lower()) student_response_arr.append(int_api_resp) return student_response_arr def run_sentiment_analysis(message_text): """ Evaluates the sentiment of a student message >>> run_sentiment_analysis("I am tired") [{'label': 'NEGATIVE', 'score': 0.9997807145118713}] >>> run_sentiment_analysis("I am full of joy") [{'label': 'POSITIVE', 'score': 0.999882698059082}] """ # TODO: Add intent labelling here # TODO: Add logic to determine whether intent labeling or sentiment analysis is more appropriate (probably default to intent labeling) return sentiment(message_text) def run_intent_classification(message_text): """ Process a student's message using basic fuzzy text comparison >>> run_intent_classification("exit") {'type': 'intent', 'data': 'exit', 'confidence': 1.0} >>> run_intent_classification("exi") {'type': 'intent', 'data': 'exit', 'confidence': 0.86} >>> run_intent_classification("eas") {'type': 'intent', 'data': '', 'confidence': 0} >>> run_intent_classification("hard") {'type': 'intent', 'data': '', 'confidence': 0} >>> run_intent_classification("hardier") {'type': 'intent', 'data': 'harder', 'confidence': 0.92} """ label = '' ratio = 0 nlu_response = {'type': 'intent', 'data': label, 'confidence': ratio} commands = [ 'easier', 'exit', 'harder', 'hint', 'next', 'stop', ] for command in commands: try: ratio = fuzz.ratio(command, message_text.lower()) except: ratio = 0 if ratio > 80: nlu_response['data'] = command nlu_response['confidence'] = ratio / 100 return nlu_response def evaluate_message_with_nlu(message_data): """ Process a student's message using NLU functions and send the result >>> evaluate_message_with_nlu({"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "8", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"}) {'type': 'integer', 'data': 8, 'confidence': 0} >>> evaluate_message_with_nlu({"author_id": "57787919091", "author_type": "OWNER", "contact_uuid": "df78gsdf78df", "message_body": "I am tired", "message_direction": "inbound", "message_id": "dfgha789789ag9ga", "message_inserted_at": "2023-01-10T02:37:28.487319Z", "message_updated_at": "2023-01-10T02:37:28.487319Z"}) {'type': 'sentiment', 'data': 'NEGATIVE', 'confidence': 0.9997807145118713} """ # Keeps system working with two different inputs - full and filtered @event object try: message_text = str(message_data['message_body']) except KeyError: message_data = { 'author_id': message_data['message']['_vnd']['v1']['chat']['owner'], 'author_type': message_data['message']['_vnd']['v1']['author']['type'], 'contact_uuid': message_data['message']['_vnd']['v1']['chat']['contact_uuid'], 'message_body': message_data['message']['text']['body'], 'message_direction': message_data['message']['_vnd']['v1']['direction'], 'message_id': message_data['message']['id'], 'message_inserted_at': message_data['message']['_vnd']['v1']['chat']['inserted_at'], 'message_updated_at': message_data['message']['_vnd']['v1']['chat']['updated_at'], } message_text = str(message_data['message_body']) # Run intent classification only for keywords intent_api_response = run_intent_classification(message_text) if intent_api_response['data']: prepare_message_data_for_logging(message_data, intent_api_response) return intent_api_response number_api_resp = text2int(message_text.lower()) if number_api_resp == 32202: # Run intent classification with logistic regression model predicted_label = predict_message_intent(message_text) if predicted_label['confidence'] > 0.01: nlu_response = predicted_label else: # Run sentiment analysis sentiment_api_resp = sentiment(message_text) nlu_response = build_nlu_response_object( 'sentiment', sentiment_api_resp[0]['label'], sentiment_api_resp[0]['score'] ) else: nlu_response = build_nlu_response_object( 'integer', number_api_resp, 0 ) prepare_message_data_for_logging(message_data, nlu_response) return nlu_response