def get_next_slot_text(self, slot_name, slot_required): try: types_with_options = u.choices_type_list slot = self.get_slot(slot_name) useful_info = '' value_type = slot[u.value_type] required_string = fn.get_required_string(slot_required) if value_type in types_with_options: choice_list = slot[u.choice_list] option_string = fn.get_string_from_list(choice_list) if value_type in [u.dropdown, u.checkbox]: string = f"Select the {slot_name} in the following list {option_string}. {required_string}" elif value_type == u.radio: string = f"Choose your {slot_name} in the following list {option_string}. {required_string}" if u.DEBUG: print("choice list") print(choice_list) else: # we construct some useful info. if value_type in [u.number, u.decimal, u.integer]: useful_info = f'\nThe value should be a number between {slot[u.min_value]} and {slot[u.max_value]}.' insert_style = styles.get_insert() please_style = styles.get_please() string = f"{please_style} {insert_style} the {slot_name}. {required_string}" string = f'{string}{useful_info}' return string except: print("ERROR: Fail to extract the text for the next slot") raise Exception
def resume_spelling(self, slot_name): try: # we update the next fields self.set_next_slot(slot_name) saved_fields = self.get_saved_spelling_fields() saved_values = self.get_saved_spelling_values() # we set the saved value as current input value index = saved_fields.index(slot_name) value = saved_values[index] self.set_current_spelling_input_value(value) if u.DEBUG: print(f'The current value for {slot_name} is {value}') # we remove the field from saved state saved_fields.remove(slot_name) saved_values.remove(value) next_style = styles.get_next() please_style = styles.get_please() string = ( f'You started filling the field {slot_name} and the current value is {value} ' + f'\n{please_style} insert the {next_style} character') # possible next action is spelling self.set_possible_next_action(u.spelling_action) # we enable the close prompt self.set_close_prompt_enabled() return string except: print('ERROR: Fail to manage the resume of the spelling') raise Exception
def verify_compatibility_integer(value, min_value=-float('inf'), max_value=float('inf')): try: value = value.replace(' ', '') int_value = get_integer(value) sorry_style = styles.get_sorry() insert_style = styles.get_insert() please_style = styles.get_please() if int_value is None: text = f'{sorry_style} the value {value} is not an integer, {please_style} {insert_style} a valid value.' return False, text number = int(int_value) if number < min_value or number > max_value: if number < min_value: text = f'{sorry_style} the value inserted is less than the minimum value acceptable {min_value}.' else: text = f'{sorry_style} the value inserted is more than the maximum value acceptable {max_value}.' return False, text return True, int_value except: print( f'Fail to verify the compatibility of the value {value} for the type integer' ) raise Exception
def verify_compatibility_number(value, min_value=-float('inf'), max_value=float('inf')): try: value = value.replace(' ', '') int_value = int(get_integer(value)) interval_text = '' if min_value != -float('inf'): interval_text = f'The minimum value for this field is {min_value}. ' if max_value != float('inf'): interval_text = f'{interval_text}The maximum value for this field is {max_value}.' if int_value is None: sorry_style = styles.get_sorry() insert_style = styles.get_insert() please_style = styles.get_please() text = f'{sorry_style} the value {value} is not a number, {please_style} {insert_style} a valid value.' return False, text if int_value < min_value or int_value > max_value: if int_value < min_value: text = f'{sorry_style} the value inserted is less than the minimum value acceptable {min_value}.' else: text = f'{sorry_style} the value inserted is more than the maximum value acceptable {max_value}.' return False, text return True, value except: print( f'Fail to verify the compatibility of the value {value} for the type number' ) raise Exception
def verify_compatibility_tel(value): try: text = value # we should not have the space in the final value if ' ' in value: text = text.replace(' ', '') if '-' in value: text = text.replace('-', '') plus_sign = "+" num_occur = value.count(plus_sign) if num_occur == 1: if value[0] != plus_sign: text = f'The character < {plus_sign} > could only be at the beginning of the number' return False, text elif num_occur > 1: text = f'A telephone number should not contain the character < {plus_sign} > several times' return False, text # we remove the eventual plus sign at the beginning number = value.replace(plus_sign, '') # we verify if the remaining string only contains digits from 0 to 9 number = fn.convert_to_int(number) if number is None or len(value) < u.min_length_phone_number: sorry_style = styles.get_sorry() insert_style = styles.get_insert() please_style = styles.get_please() text = f'{sorry_style} the value {value} is not valid, {please_style} {insert_style} a valid phone number' return False, text return True, text except: print( f'Fail to verify the compatibility of {value} for the type phone number' ) raise Exception
def next_char_string(): try: # we add styles to the output next_style = styles.get_next() please_style = styles.get_please() insert_style = styles.get_insert() end_style = styles.get_end() # we set the message to be returned to the user string = (f'{please_style} {insert_style} the {next_style} character, remember that you can use the expression SPACE for the blank' + f' and the expression TERMINATE to {end_style} the spelling') return string except: print('Fail to get the string for asking the next character') raise Exception
def manage_next_step(self): try: slot_name, next_slot_required = self.get_next_slot() if slot_name is not None: if u.DEBUG: print("The next slot is: " + slot_name) # to observe the modification that happened print(self.get_slots_value()) string = "{}".format( self.get_next_slot_text(slot_name, next_slot_required)) if slot_name in self.get_spelling_fields() and u.USE_SPELLING: # we add the field to the spelling list self.add_spelling_name(slot_name) # we verify if the field has been previously saved saved_fields = self.get_saved_spelling_fields() if u.DEBUG: print(f'The saved fields are {saved_fields}') if slot_name in saved_fields: # we set the saved value and we get the next step string return self.resume_spelling(slot_name) else: please_style = styles.get_please() string = ( f'{string}\nSince it is a field requiring the spelling, we are going to take' + f' its value one character at a time.\n{please_style} insert the first character:' ) # possible next action is spelling self.set_possible_next_action(u.spelling_action) return string else: # possible next action is fillGenericCamp self.set_possible_next_action(u.fill_field_action) return string else: # the submit button here is one shot so we enable the alarm self.set_submit_alarm_enabled() string = self.submit_string() self.set_possible_next_action(u.submit_action) return string except: print("ERROR: Fail to get the string for the next field") raise Exception
def verify_compatibility_decimal(value, precision=2, min_value=-float('inf'), max_value=float('inf')): try: value = value.replace(' ', '') dec_value = get_decimal(value) sorry_style = styles.get_sorry() insert_style = styles.get_insert() please_style = styles.get_please() if dec_value is None: text = f'{sorry_style} the value {value} is not a decimal, {please_style} {insert_style} a valid value.' return False, text # float takes the 'dot' as separator, so we should transform it before getting the float number = float(dec_value.replace(',', '.')) if number < min_value or number > max_value: if number < min_value: text = f'{sorry_style} the value inserted is less than the minimum value acceptable {min_value}.' else: text = f'{sorry_style} the value inserted is more than the maximum value acceptable {max_value}.' return False, text if ',' not in dec_value: return True, dec_value # the value has a decimal part if precision == float('inf'): return True, dec_value dec_part = dec_value[dec_value.index(',') + 1:] if len(dec_part) <= 2: # the precision is respected return True, dec_value # the precision is more accurate then what required, we decide to troncate, no rounding dec_value = dec_value[:dec_value.index(',') + precision + 1] return True, dec_value except: print( f'Fail to verify the compatibility of the value {value} for the type decimal with precision {precision}' ) raise Exception
def verifyPresenceOfField(self): try: entities = self.state.get_latest_message()["entities"] count = len(entities) if count == 0: # in principle should never occur given the training please_style = styles.get_please() text = f"{please_style} indicate which field you are interested to," _, required = self.state.get_next_slot() if not required: text = f'{text} otherwise, you can {u.fun_submit}, {u.fun_verify_presence_field} or {u.fun_skip}' else: text = f'{text} otherwise, you can {u.fun_verify_value} or {u.fun_form_description}' self.state.set_warning_message(text) raise Exception slot_name_list = fn.extract_fields_names_and_values( entities, only_names=True) # we first verify if each slot_name corresponds to a field in the dorm correct, string = self.state.all_fields_present(slot_name_list) if not correct: return string good_style = styles.get_good() string = f"{good_style}, " for name in slot_name_list: text = fn.verify_presence(name, self.state.form_slots(), only_text=True) string = f'{string}, {text}' next_step_string = self.state.manage_next_step() string = f'{string}\n{next_step_string}' return string except: if not self.state.get_warning_present(): print( "A problem occured while a registration form bot tries to verify the presence of a label" ) raise Exception
def set_choices_checkbox(self, slot_name, choice_values): try: slot = self.get_slot(slot_name) choice_name = slot[u.value_name].lower() choices_lower = [] for choice_value in choice_values: choices_lower.append(choice_value.lower()) elems = self.form_element.find_elements_by_name(choice_name) value_present = False for elem in elems: value = elem.get_attribute("value") if value in choices_lower: if not elem.is_selected(): elem.click() value_present = True else: if elem.is_selected(): elem.click() value_present = True if not value_present: choices_string = fn.get_string_from_list(choice_values) sorry_style = styles.get_sorry() please_style = styles.get_please() text = ( f"{sorry_style} none of the choices {choices_string} you proposed is valid for the field {slot_name}" + f"{please_style} choose them in the following list: {choice_list}" ) self.set_warning_message(text) raise Exception except: if not self.get_warning_present(): choices_list = fn.get_string_from_list(choice_values) print( f"ERROR: A problem occured while trying to set the choices {choices_list} in the checkbox with name {choice_name}" ) raise Exception
def fillSpellingField(self): try: if len(self.state.get_spelling_list()) == 0: # misinterpretation text = 'I did not get you well, could you precise your action please?' _, required = self.state.get_next_slot() if not required: text = f'{text} otherwise, you can {u.fun_skip}, {u.fun_reset} or {u.fun_submit}' else: text = f'{text} otherwise, you can {u.fun_remaining_fields} or {u.fun_modify_field}' self.state.set_warning_message(text) raise Exception # we have to verify if we just finished the spelling of a field if self.state.get_after_spelling(): # after spelling is desabled since we are going to insert the vale for a field self.state.set_after_spelling(False) slot_name = self.state.get_next_slot(only_name=True) slot_value = self.state.get_current_spelling_input_value() # we go to the filling procedure. The spelling list can be modified in this phase string = self.state.filling_procedure(slot_name, slot_value) if 'inserted!' in string.lower() and 'not' in string.lower(): self.state.reset_current_spelling_input_value() return string # verify if all the fields in spelling list have been completed if len(self.state.get_spelling_list()) - 1 == 0: self.state.reset_spelling_list() else: # there are still fields to spell self.state.update_spelling_list(slot_name) next_field = self.state.get_spelling_list()[0] please_style = styles.get_please() string = ( f"Web Form updated. Now you are going to spell the value for the field {next_field}.\n" + f"{please_style} insert the first character") # after the spelling we insert the value for the given field and we reset the string self.state.reset_current_spelling_input_value() return string """we are not after spelling so the fillGenericField directly called this function(at the previous step it was triggered by the spelling function) or it is triggered by the mofifySpellingField function""" # we look if one of the fields in spelling fields has been saved to resume it. In case of many fields saved we take the first slot_name_list = self.state.get_spelling_list() saved_fields = self.state.get_saved_spelling_fields() if len(saved_fields) > 0: # there is at least one saved field for field in slot_name_list: if field in saved_fields: string = self.state.resume_spelling(field) return string # we set the first spelling field of the list as the next field slot_name = slot_name_list[0] self.state.set_next_slot(slot_name) # we add styles to the output please_style = styles.get_please() end_style = styles.get_end() fields_string = fn.get_string_from_list(slot_name_list) if len(slot_name_list) == 1: intro = f"You will have to spell the value of the field {slot_name_list[0]}." else: intro = ( f"You will have to spell the values of the following fields {fields_string}.\n" + f"We start by the field {slot_name_list[0]}.") # we set the message to be returned to the user string = ( f'{intro}\n{please_style} insert the first character, you will be able to use SPACE for spacing' + f'and TERMINATE to {end_style} the spelling') self.state.set_possible_next_action(u.spelling_action) return string except: if not self.state.get_warning_present(): print( "A problem occured while a registration form bot tries to fill a spelling camp" ) raise Exception
def fillGenericField(self): try: entities = self.state.get_latest_message()["entities"] intent = self.state.get_latest_message()["intent"]["name"] count = len(entities) if count == 0: # the user wants to modify a field but did not specify which field modify_style = styles.get_modify() insert_style = styles.get_insert() string = ( f"Which field exactly do you want to {modify_style}, and which value" + f" do you want to {insert_style} for that field?") self.state.set_possible_next_action(u.fill_field_action) return string slot_name_list, slot_value_list = fn.extract_fields_names_and_values( entities) # we first verify if each slot_name corresponds to a field in the dorm correct, string = self.state.all_fields_present(slot_name_list) if not correct: return string """we extract now the spelling field and we insert them in the spelling list after that we fill the generic fields before passing the floor to fillSpellingField to complete the fields in spelling list.""" slot_name_list, slot_value_list = self.state.set_spelling_list( slot_name_list, slot_value_list) # the spelling list have been set and we can continue if len(slot_value_list) + len(slot_name_list) == 0: string = self.fillSpellingField() return string if len(slot_value_list) + len(slot_name_list) >= 1: # there is at least one generic info # the use of possible_next_action here is to mitigate training error. with a perfect training they are not necessary if len(slot_value_list) == 1 and len(slot_name_list) == 0 and ( self.state.get_possible_next_action() == u.fill_field_action or intent == u.fill_field_action): # we are inserting a value for the current field slot_name = self.state.get_next_slot( only_name=True ) # return also if the next slot is required or not # We make verify if the current field is spelling spelling_fields = self.state.get_spelling_fields() if slot_name in spelling_fields: string = self.fillSpellingField() return string slot_value = slot_value_list[0] # the slot value can change, being put in the right format # we go to the filling procedure insuring that the field is not spelling string = self.state.filling_procedure( slot_name, slot_value) elif self.state.get_possible_next_action( ) != u.fill_field_action and intent != u.fill_field_action: # probably bad destination due to misinterpretation sorry_style = styles.get_sorry() please_style = styles.get_please() text = f'{sorry_style} i do not understand your request, could you reformulate {please_style}?' _, required = self.state.get_next_slot() if not required: text = f'{text} otherwise, you can {u.fun_skip} or {u.fun_submit}' else: text = f'{text} otherwise, you can {u.fun_explain_field} or {u.fun_complete_field}' self.state.set_warning_message(text) raise Exception else: # we have a list of fields with their values string = self.state.fill_generic_slots( slot_name_list=slot_name_list, slot_value_list=slot_value_list) next_field_before = self.state.get_next_slot(only_name=True) # we verify if the spelling_list is empty or not if len(self.state.get_spelling_list()) != 0: string_spelling = self.fillSpellingField() next_field_after = self.state.get_next_slot(only_name=True) if next_field_before == next_field_after: # the spelling did not modified the Web page, we return the string from the non spelling fields return string # the spelling mofified the Web Form, so we use its string return string_spelling return string except: if not self.state.get_warning_present(): print( "A problem occured while a registration form bot tries to fill a camp" ) raise Exception
def fill_input(self, slot_name, slot_value): # returns the slot value eventually modified to be in the right format, and a string to # eventuelly mention some incopatibilities try: string = "" # substitute the underscores with spaces to outpu the right message input_type_list = u.input_type_list slot = self.get_slot(slot_name) value_type = slot[u.value_type] value_name = slot[u.value_name] # the slot_value is not None if u.DEBUG: print(f"value_name: {value_name}, value_type: {value_type}") compatible, text = fn.is_compatible(slot_value, slot) if not compatible: next_step_string = self.manage_next_step() string = f'{text}\n{next_step_string}' return None, string # the value and the type are compatible so it is pssible that the value has been converted # to an appropriate form contained in text slot_value = text if value_type in input_type_list: elem = self.form_element.find_element_by_name(value_name) elem.clear() if slot_value is None: return "", string elem.send_keys(slot_value) return slot_value, string # the value_type is in u.choices_list # the value is put in lowercase to coincide with the choice slot_value = slot_value.lower() if u.DEBUG: print(f"choice: {slot_value}") # we verify that slot_value is in the list of choices choice_list = slot[u.choice_list] length = len(choice_list) for index in range(length): choice_list[index] = choice_list[index].lower() if slot_value not in choice_list: sorry_style = styles.get_sorry() please_style = styles.get_please() string = ( f"{sorry_style} the choice {slot_value} is not valid for the field {slot_name}" + f"{please_style} choose one in the following list: {choice_list}" ) self.set_possible_next_action(u.fill_field_action) return None, string # we are sure that the choice of the user is in the list of choices if value_type == u.dropdown: self.set_choice_dropdown(slot_name=slot_name, choice_value=slot_value) elif value_type == u.checkbox: self.set_choice_checkbox(slot_name=slot_name, choice_value=slot_value) elif value_type == u.radio: self.set_choice_radio(slot_name=slot_name, choice_value=slot_value) return slot_value, string except: next_step_string = self.manage_next_step() string = f'The value {slot_value} is not valid for the field {slot_name}.\n{next_step_string}' return None, string