def after_submit(self): try: """Here we conclude the submission of the form and then we start or not a new dialogue. If we start a new dialogue, we restart from the beginning. Each time, the state is saved to write the final report""" # we increment the nunmer of iteration self.iteration_number += 1 newPageTitle = self.driver.title good_style = styles.get_good() text = f"{good_style} you have been moved to the page with title {newPageTitle}" self.chatbot_view.show_text(self.counter, text) self.counter += 1 # we conclude this log self.conclude_log() # we add the state to the list state = self.current_bot.get_state() # we insert the number of not properly handled messages inside the state state.constructs[u.form_construct][ u.number_not_handled] = self.number_not_handled self.states_list.append(state) # we sample the reports not to loose everything in case of problem if self.iteration_number in [50]: # write the report w.ReportWriter(self.states_list).start() self.write_log() if self.iteration_number >= self.total_iterations: self.restart = False self.in_session = False return restart = None while restart is None: text = '\n[ALERT: Would you like to start a new dialogue ?\t1- Yes\t0- No\n>>> Response: ' if u.simulation_enabled: dialogue_state = self.get_dialogue_state() answer, _ = self.user.get_answer(dialogue_state) else: answer = input(text) try: restart = int(fn.convert_to_int(answer)) if restart is None: sorry_style = styles.get_sorry() print(f'{sorry_style} your input is not valid') except: sorry_style = styles.get_sorry() print(f'{sorry_style} your input is not valid') if restart: good_style = styles.get_good() print(f'{good_style} we are going to start a new dialogue') self.restart = True # in any case here in_session should be False to stop the dialogue # then we can restart a new dialogue or completely finish self.update_parameters() else: self.restart = False self.in_session = False except: print('Fail to manage the after submission') raise Exception
def affirm(self): try: # we have to verify the state to know what is the affirm for. the variable to check # is possible_next_action. action_name = self.state.get_possible_next_action() if action_name in [u.submit_action, u.reset_all_fields_action]: action = self.get_action(action_name) string = action(self) return string elif action_name is None: sorry_style = styles.get_sorry() text = f'{sorry_style} what exactly do you want to do? If you want, you can:\n{fn.get_functionalities_list()}' self.state.set_warning_message(text) raise Exception string = self.state.manage_next_step() _, required = self.state.get_next_slot() if not required: string = f'{string} otherwise, you can {u.fun_all_fields}, {u.fun_remaining_required_fields} or {u.fun_skip} ' else: string = f'{string} otherwise, you can {u.fun_remaining_required_fields} or {u.fun_form_title}' return string except: if not self.state.get_warning_present(): print( "A problem occured while a registration form bot tries to get an affirmation" ) 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 get_form_description(self): try: requested_slot = self.get_slot(u.REQUESTED_SLOT) desc = requested_slot[u.description] if desc is None: sorry_style = styles.get_sorry() text = f"{sorry_style} there is no explanation provided for this form" return text # the description is present in the html file return desc except: print('ERROR: Fail to get the description of the form') raise Exception
def get_form_title(self): try: requested_slot = self.get_slot(u.REQUESTED_SLOT) title = requested_slot[u.title] if title is None: sorry_style = styles.get_sorry() text = f"{sorry_style} there is no title provided for this form" return text # the title is present in the html file return title except: print('ERROR: Fail to get the title of the form') raise Exception
def get_field_description(self, field): try: slot = self.get_slot(field) desc = slot[u.description] if desc is None: sorry_style = styles.get_sorry() text = f"There is no explanation provided for the field {field} {sorry_style}" return text # the description is present in the html file text = f'Here is the explanation provided for the {field}: {desc}' return text except: print(f'ERROR: Fail to get the description of the field {field}') raise Exception
def verify_presence(name, slots, only_presence=False, only_text=False): try: possible_names = [] for slot in slots: slot_name = slot[u.slot_name] possible_names.append(slot_name) if name in possible_names: text = f"The field {name} is present." if only_presence: return True elif only_text: return text else: return text, True alternatives = [] for pos in possible_names: if name in pos: alternatives.append(pos) if len(alternatives) > 0: string_alt = get_string_from_list(alternatives) if len(alternatives) == 1: text = f"The field {name} is not present but you have this alternative {string_alt}." else: text = f"The field {name} is not present but you have these alternatives {string_alt}." if only_presence: return False elif only_text: return text else: return text, False sorry_style = styles.get_sorry() text = f"The field {name} is not present {sorry_style}" if only_presence: return False elif only_text: return text else: return text, False except: print(f'Fail to verify the presence of {name}') raise Exception
def verifyValueFilledFields(self): try: slots = self.state.form_slots() filled_string = fn.get_pairs(slots, only_filled=True) next_step_string = self.state.manage_next_step() if filled_string == '': sorry_style = styles.get_sorry() string = f'{sorry_style} up to now you did not complete any field\n{next_step_string}' else: string = ( f"The fields you already completed are the following: \n{filled_string}\n" + f"If you see some stars, they indicate the required fields.\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 value of the filled camps" ) 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 repeatFormExplanation(self): try: form_desc = self.state.get_form_description() next_step_string = self.state.manage_next_step() if form_desc is None: sorry_style = styles.get_sorry() string = f"{sorry_style} this form does not have a description.\n{next_step_string}" _, required = self.state.get_next_slot() if not required: string = f'{string} otherwise, you can {u.fun_skip}' else: string = f'{string} otherwise, you can {u.fun_recap} or {u.fun_verify_value}. In any case you will have to complete this field in order to submit' else: sure_style = styles.get_sure() string = f"{sure_style} here it is: {form_desc}." 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 repeat the form's explanation" ) raise Exception
def all_fields_present(self, slot_name_list): try: # returns True if all the fields are present and False otherwise. In ca of false retruns also the next_step string string = '' for slot_name in slot_name_list: present = fn.verify_presence(slot_name.lower(), self.form_slots(), only_presence=True) if not present: sorry_style = styles.get_sorry() list_fields = self.get_fields_list() string_fields = fn.get_string_from_list(list_fields) text = ( f"{sorry_style} the field {slot_name} is not present in this form.\nThe fields of this form are" + f" the following: {string_fields}") next_step_string = self.manage_next_step() string = f'{text}\n{next_step_string}' return False, string return True, string except: if not self.get_warning_present(): print('ERROR: Fail to verify presence of all fields') raise Exception
def skipField(self): try: # we set the actual slot to campare it later with the next slot actual_slot_name = self.state.get_next_slot( only_name=True ) # return also if the next slot is required or not # we get the next slot without having filled the current slot and we verify if it is the last field self.state.set_next_slot_basic() next_slot_name, next_slot_required = self.state.get_next_slot( ) # return also if the next slot is required or not if actual_slot_name == next_slot_name: sorry_style = styles.get_sorry() text = f"{sorry_style} this field is the last one remaining." if not next_slot_required: opt = "Do you want to submit now?" self.state.set_possible_next_action(u.submit_action) string = f'{text}{opt} If yes, give an affirmative response otherwise {self.state.manage_next_step()}' else: string = ( f"{text} it is required to be able to submit the Web Form.\n" + f"{self.fillForm()}") return string # we got to the next step string = self.state.manage_next_step() fields_remaining = self.state.get_fields_list(remaining=True) if self.state.get_all_required_filled( ) and len(fields_remaining) <= 2: string = f'If you want, you can {u.fun_submit}, otherwise {string}' else: string = f'The {actual_slot_name} has been skipped.\n{string}' return string except: if not self.state.get_warning_present(): print( "A problem occured while a registration form bot tries to skip a camp" ) 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 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
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