def preference_extractor(self, user_input): """ Parameters ---------- user_input : str user utterance. Returns ------- user_preferences : list list of user preferences extracted from utterance. p[0] for the area, p[1] for the price range and p[2] for the food type """ user_preferences = [ 0 for i in range(3) ] #the preferences are stored in a list of three elements, p[0] for the area, p[1] for the price range and p[2] for the food type s = self.stopwords_removal(user_input) user_preferences = self.no_preference( user_input, user_preferences) #check if user indicated no preference #keyword matching for the area for i in s: for j in self.area: if i == j: user_preferences[0] = j if (('north' and 'american' in s) and (s.count('north')) > 1): user_preferences[0] = 0 #keyword matching for the price range for i in s: for j in self.price_range: if i == j: user_preferences[1] = j #keyword matching for the food type for i in s: for j in self.food_types: if i == j: user_preferences[2] = j elif ('asian' and 'oriental' in s): user_preferences[2] = 'asian oriental' elif ('modern' and 'european' in s): user_preferences[2] = 'modern european' elif ('north' and 'american' in s): user_preferences[2] = 'north american' #In case the area has been mispelt if (user_preferences[0] == 0): d = {} l = [] z = ['south', 'centre', 'west', 'east', 'north'] for i in s: for j in z: if (dt(i, j) <= 2) and i != 'want' and i != 'eat': d[j] = dt(i, j) for i in d.values(): l.append(i) if len(l) > 0: k = min(l) key_list = list(d.keys()) val_list = list(d.values()) if k <= 2: user_preferences[0] = key_list[val_list.index(k)] #In case the price range has been mispelt if (user_preferences[1] == 0): d = {} l = [] d = {} l = [] for i in s: for j in list(set(self.price_range)): if (dt(i, j) <= 3): d[j] = dt(i, j) for i in d.values(): l.append(i) if len(l) > 0: k = min(l) key_list = list(d.keys()) val_list = list(d.values()) if k <= 2: user_preferences[1] = key_list[val_list.index(k)] #In case the food type has been mispelt #thresolds for Levenshtein distances might need to be better tuned for each preference if (user_preferences[2] == 0): d = {} l = [] for i in s: for j in list(set(self.food_types)): if (dt(i, j) <= 2): d[j] = dt(i, j) elif (dt('asian', i) <= 2 or dt('oriental', i) <= 2 in s): d['asian oriental'] = min( [dt('asian', i), dt('oriental', i)]) elif (dt('modern', i) <= 2 or dt('european', i) <= 2 in s): d['modern european'] = min( [dt('modern', i), dt('european', i)]) elif (dt('north', i) <= 2 or dt('american', i) <= 2 in s): if ('north' and 'american' in s): d['north american'] = min( [dt('north', i), dt('american', i)]) for i in d.values(): l.append(i) if len(l) > 0: k = min(l) key_list = list(d.keys()) val_list = list(d.values()) if k <= 3: user_preferences[2] = key_list[val_list.index(k)] return user_preferences
def dialogue(self, user_input, state, user_preferences): """ recursive state transition function. Parameters ---------- user_input : str DESCRIPTION. state : str State of the system. user_preferences : list list of user preferences (area,price,foodtype). Returns ------- None. """ if user_input in [ "configure formal", "configure delay", "configure informal", "configure no delay" ]: self.configure(user_input) user_input = "" self.dialogue(user_input, state, user_preferences) time.sleep(self.delay) self.statelog.append( [user_input, state] ) #tuple of user utterance and its associated state. We use this to keep track of state jumps. if state == "exit": print("Dialog Agent: " + random.choice(self.responses.get("Goodbye"))) return if state in ("init"): user_preferences = [0, 0, 0] user_input = input("Dialog Agent: " + random.choice(self.responses.get("Welcome")) + "User: "******"inform", "reqalts", 'hello'): extracted_preferences = self.preference_extractor(user_input) for i, d in enumerate(user_preferences): if d == 0: user_preferences[i] = extracted_preferences[i] state = "fill_blanks" #if more slots to be filled self.suggestions = self.lookup(user_preferences) if (len(self.suggestions) == 0) or (len(self.suggestions) == 1): state = "answer" #if there is none or 1 restaurant to suggest self.dialogue(user_input, state, user_preferences) return if state == "fill_blanks": #ask user for area/foodtype/pricerange grounding = self.grounding(user_preferences) if user_preferences[0] == 0: user_input = input("Dialog Agent: " + grounding + random.choice(self.responses.get("Area")) + "User: "******"area" not in user_input: user_input += " area" if "dont care" in user_input: user_input = 'any area' elif user_preferences[1] == 0: user_input = input("Dialog Agent: " + grounding + random.choice(self.responses.get("Price")) + "User: "******"price" not in user_input: user_input += " price" if "dont care" in user_input: user_input = 'any price' elif user_preferences[2] == 0: user_input = input("Dialog Agent: " + grounding + random.choice(self.responses.get("Food")) + "User: "******"food" not in user_input: user_input += " food" if "dont care" in user_input: user_input = 'any food' else: state = 'ask_extra_preferences' self.dialogue(user_input, state, user_preferences) return if state == 'ask_extra_preferences': state = self.ask_extra_preferences(user_preferences) self.dialogue(user_input, state, user_preferences) return if state == "confirmpreferences": user_input = input( "Dialog Agent: " + random.choice(self.responses.get("AffirmPreferences")).format( user_preferences[0], user_preferences[1], user_preferences[2]) + "User: "******"answer" elif accept is False: state = "inform" user_input = "" user_preferences = [0, 0, 0] elif accept == "reqalts": user_preferences = [0, 0, 0] else: state = "accept" self.dialogue(user_input, state, user_preferences) return if state == "answer": if self.suggestions: #found at least 1 restaurant user_input = input("Dialog Agent: " + self.suggest_restaurant() + "User: "******"ack", "affirm"]: state = "goodbye" elif state in ["reqalts", "reqmore", "deny", "negate"]: state = "answer" else: #no restaurants found. Search for alternatives alternatives = self.get_alternative_restaurants( self.alternative_preferences( user_preferences)) #offer alternatives if len(alternatives) == 1: #found 1 alternative print("Dialog Agent: " + random.choice(self.responses.get("NoOptions")) + "Let me look for an alternative for you...\n") self.suggestions = alternatives self.recommendation = self.suggestions[0] user_input = input("Dialog Agent: " + self.suggest_restaurant() + "User: "******"goodbye" elif alternatives: #found multiple alternatives print("Dialog Agent: " + random.choice(self.responses.get("NoOptions")) + "Here is a list of alternatives:") for a in alternatives: print("Dialog Agent: " + self.get_restaurant_info(a)) user_input = input( "Dialog Agent: " + 'Would you like to choose one (1) or change your preferences(2)?\n' + "User: "******"1": user_input = input( "Dialog Agent: " + "Which one would you like to choose?\n" + "User: "******"thankyou" elif user_input == "2": user_preferences = [0, 0, 0] state = 'inform' elif user_input == "exit": state = 'exit' else: print("Dialog Agent: " + "Please choose one of the two options") else: #didnt find any alternative print("Dialog Agent: " + random.choice(self.responses.get("NoOptions"))) user_preferences = [0, 0, 0] state = 'inform' user_input = "" self.dialogue(user_input, state, user_preferences) return if state in ["reqalts", "thankyou", "goodbye", "reset"]: user_input = input( "Dialog Agent: " + self.get_restaurant_contacts(self.recommendation) + ". Would you like to finish here?\n" + "User: "******"ack", "affirm")): state = "exit" else: state = "init" self.dialogue(user_input, state, user_preferences) return if state == "repeat": try: user_input = self.statelog[len(self.statelog) - 3][0] state = self.statelog[len(self.statelog) - 3][1] except IndexError: print("Dialog Agent: " + "Nowhere to go back, starting again\n") state = "init" self.dialogue(user_input, state, user_preferences) return else: print( "Dialog Agent: " + "I could not understand that, could you phrase it differently?" ) #statelog[len(statelog) + 1][0] state = self.statelog[len(self.statelog) - 2][1] self.dialogue(user_input, state, user_preferences) return