def from_input(cls, argument: str, user: User, sub_repo) -> 'E621TaggingSource': parsed = InputParser(argument) tags_arg = parsed.get_arg_by_names([ "tags", "watched_tags", "to_tag", "watched tags", "to tag", "watch" ]) search_arg = parsed.get_arg_by_names([ "search", "query", "search_query", "search query", "subscription", "sub", "search_term", "search term", ]) if tags_arg is not None: tags = tags_arg.split() if search_arg is not None: search = search_arg else: search = parsed.remaining_text else: if search_arg is not None: search = search_arg tags = parsed.remaining_text.split() else: raise hallo.modules.subscriptions.subscription_exception.SubscriptionException( 'You need to specify a search term with search="search term" and ' 'tags to watch with tags="tags to watch"') return E621TaggingSource(search, tags)
def test_mismatched_quotes(): p = InputParser('yo \'base unit"="hello world"') assert p.remaining_text == "yo 'base" assert p.args_dict['unit"'] == "hello world" p = InputParser("yo 'base unit'=\"hello's world\"") assert p.remaining_text == "yo" assert p.args_dict["base unit"] == "hello's world"
def test_get_arg_by_names_first_match(): p = InputParser("blah blah arg1=val1 arg2=val2 arg3=val3") assert p.remaining_text == "blah blah" assert p.get_arg_by_names(["arg1", "arg2"]) == "val1" assert p.args_dict["arg1"] == "val1" assert p.args_dict["arg2"] == "val2" assert p.args_dict["arg3"] == "val3"
def run(self, event): # Load repo, clean line function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function) # type: Convert repo = convert_function_obj.convert_repo # Parse input parsed = InputParser(event.command_args) # Check if unit is defined unit_name = parsed.get_arg_by_names(self.NAMES_UNIT) # Check if type is defined type_obj = None type_name = parsed.get_arg_by_names(self.NAMES_TYPE) if type_name is not None: type_obj = repo.get_type_by_name(type_name) if type_obj is None: return event.create_response("Invalid type specified.") # Check if delete name is specified del_name = parsed.get_arg_by_names(self.NAMES_DEL) if del_name is None: del_name = parsed.remaining_text # Check if description is sufficient to narrow it to 1 and only 1 unit unit_list = (repo.get_full_unit_list() if type_obj is None else type_obj.get_full_unit_list()) user_unit_options = [] for unit_obj in unit_list: # if unit name is defined and not a valid name for the unit, skip it. if unit_name is not None and not unit_obj.has_name(unit_name): continue # If input_name is not a valid name for the unit, skip it. if not unit_obj.has_name(del_name): continue # Otherwise it's the one, add it to the list user_unit_options.append(unit_obj) # Check if that narrowed it down correctly. if len(user_unit_options) == 0: return event.create_response( "There are no units matching that description.") if len(user_unit_options) >= 2: return event.create_response( "It is ambiguous which unit you refer to.") # Check this unit has other names. user_unit = user_unit_options[0] if len(user_unit.name_list) == 1: return event.create_response( "This unit only has 1 name, you cannot remove its last name.") # Remove name user_unit.remove_name(del_name) # Save repo repo.save_json() # Output return event.create_response( 'Removed name "{}" from "{}" unit.'.format(del_name, user_unit.name_list[0]))
def test_parse_string_all_numbers(): p = InputParser("5 421 8916 34.5 -3") assert p.remaining_text == "5 421 8916 34.5 -3" assert len(p.args_dict) == 0 assert len(p.string_words) == 0 assert len(p.number_words) == 5 assert p.number_words == [5, 421, 8916, 34.5, -3]
def run(self, event): # Load convert repo function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function) # type: Convert repo = convert_function_obj.convert_repo # Parse input parsed = InputParser(event.command_args) # If decimals is null, return error if len(parsed.number_words) == 0: return event.create_response( "Please specify a conversion type and a number of decimal places it should output." ) # Convert decimals to integer decimals = int(parsed.number_words[0]) # Get selected type input_type = repo.get_type_by_name(" ".join(parsed.string_words)) # If type does not exist, return error if input_type is None: return event.create_response( "This is not a recognised conversion type.") # Set decimals input_type.decimals = decimals # Save repo repo.save_json() # Output message return event.create_response( "Set the number of decimal places to display for " + '"{}" type units at {} places.'.format(input_type.name, decimals))
def test_parse_string_mix_of_numbers_and_args(): p = InputParser("blah blah arg1=val1 arg2=val2 5") assert p.remaining_text == "blah blah 5" assert p.args_dict["arg1"] == "val1" assert p.args_dict["arg2"] == "val2" assert p.string_words == ["blah", "blah"] assert p.number_words == [5]
def test_parse_string_no_numbers(): p = InputParser("blah bloo blee") assert p.remaining_text == "blah bloo blee" assert len(p.args_dict) == 0 assert len(p.string_words) == 3 assert len(p.number_words) == 0 assert p.string_words == ["blah", "bloo", "blee"]
def test_all_types(): p = InputParser( "yo 'base unit'=\"hello world\" arg1='value 1' 'arg 2'=val2 arg3=val3") assert p.remaining_text == "yo" assert p.args_dict["base unit"] == "hello world" assert p.args_dict["arg1"] == "value 1" assert p.args_dict["arg 2"] == "val2" assert p.args_dict["arg3"] == "val3"
def add_unit(self, user_input, ref_measure_list): """ :type user_input: str :type ref_measure_list: list[ConvertMeasure] :rtype: str """ # Check reference measure has exactly 1 unit option if len(ref_measure_list) == 0: return "There is no defined unit matching the reference name." if len(ref_measure_list) > 1: return "It is ambiguous which unit you are referring to." # Get unit type ref_measure = ref_measure_list[0] ref_amount = ref_measure.amount ref_unit = ref_measure.unit ref_type = ref_unit.type ref_value = ref_unit.value ref_offset = ref_unit.offset base_unit = ref_type.base_unit base_name = base_unit.name_list[0] # Get amount & unit name # TODO: accept calculation parsed = InputParser(user_input) if len(parsed.number_words) != 1: return "Please specify an amount when setting a new unit." input_amount_float = float(parsed.number_words[0]) # Remove amountString from userInput input_name = " ".join(parsed.string_words) # Check name isn't already in use. if ref_type.get_unit_by_name(input_name) is not None: return "There's already a unit of that type by that name." # Add unit new_unit = hallo.modules.convert.convert_repo.ConvertUnit( ref_type, [input_name], 1) ref_type.add_unit(new_unit) # Update offset or value, based on what the user inputed. # If either given amount are zero, set the offset of varUnit. if input_amount_float == 0 or ref_amount == 0: # Calculate the new offset new_offset = ref_offset + ref_amount * ref_value - input_amount_float * 1 new_unit.update_offset(new_offset) # Save repo repo = ref_unit.type.repo repo.save_json() # Output message return "Created new unit {} with offset: 0 {} = {} {}.".format( input_name, input_name, new_offset, base_name) # Get new value new_value = (ref_offset + ref_amount * ref_value - 0) / input_amount_float new_unit.update_value(new_value) # Save repo repo = ref_unit.type.repo repo.save_json() # Output message return "Created new unit {} with value: 1 {} = {} {}.".format( input_name, input_name, new_value, base_name)
def run(self, event): # Load repo, clean line function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function ) # type: Convert repo = convert_function_obj.convert_repo # Clean input line_clean = event.command_args.strip() parsed_input = InputParser(line_clean) # Check if base unit is defined unit_name = parsed_input.get_arg_by_names(self.NAMES_BASE_UNIT) # Check if decimal places is defined decimals = parsed_input.get_arg_by_names(self.NAMES_DECIMALS) decimals = int(decimals) if decimals is not None else None # Clean unit and type setting from the line to just get the name to remove input_name = parsed_input.remaining_text # Check that type name doesn't already exist. existing_type = repo.get_type_by_name(input_name) if existing_type is not None: return event.create_response("A type by this name already exists.") # Check base unit name was defined. if unit_name is None: return event.create_response( "You must define a base unit for this type using unit=<unit name>." ) # Create new type, Create new unit, set unit as base unit, set decimals new_type = hallo.modules.convert.convert_repo.ConvertType(repo, input_name) new_base_unit = hallo.modules.convert.convert_repo.ConvertUnit(new_type, [unit_name], 1) new_type.base_unit = new_base_unit if decimals is not None: new_type.decimals = decimals # add type to repo, save repo.add_type(new_type) repo.save_json() # Output message decimal_string = "" if decimals is not None: decimal_string = " and {} decimal places".format(decimals) output_string = 'Created new type "{}" with base unit "{}"{}.'.format( input_name, unit_name, decimal_string ) return event.create_response(output_string)
def run(self, event): # Load repo function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function) # type: Convert repo = convert_function_obj.convert_repo # Parse input parsed = InputParser(event.command_args) # Check if type is specified type_name = parsed.get_arg_by_names(self.NAMES_TYPE) unit_name = parsed.get_arg_by_names(self.NAMES_UNIT) prefix_group_name = parsed.get_arg_by_names(self.NAMES_PREFIXGROUP) prefix_name = parsed.get_arg_by_names(self.NAMES_PREFIX) if type_name is not None: # Get type name and object type_obj = repo.get_type_by_name(type_name) if type_obj is None: return event.create_response("Unrecognised type specified.") # Check if unit & type are specified if unit_name is not None: # Get unit name and object unit_obj = type_obj.get_unit_by_name(unit_name) if unit_obj is None: return event.create_response( "Unrecognised unit specified.") return event.create_response( self.output_unit_as_string(unit_obj)) # Type is defined, but not unit. return event.create_response(self.output_type_as_string(type_obj)) # Check if prefix group is specified if prefix_group_name is not None: # Check if prefix & group are specified prefix_group_obj = repo.get_prefix_group_by_name(prefix_group_name) if prefix_group_obj is None: return event.create_response( "Unrecognised prefix group specified.") # Check if prefix group & prefix are specified if prefix_name is not None: # Get prefix name and object prefix_obj = prefix_group_obj.get_prefix_by_name( prefix_name) or prefix_group_obj.get_prefix_by_abbr( prefix_name) if prefix_obj is None: return event.create_response( "Unrecognised prefix specified.") return event.create_response( self.output_prefix_as_string(prefix_obj)) # Prefix group is defined, but not prefix return event.create_response( self.output_prefix_group_as_string(prefix_group_obj)) # Check if unit is specified if unit_name is not None: output_lines = [] # Loop through types, getting units for each type for type_obj in repo.type_list: unit_obj = type_obj.get_unit_by_name(unit_name) # If unit exists by that name, add the string format to output list if unit_obj is not None: output_lines.append(self.output_unit_as_string(unit_obj)) if len(output_lines) == 0: return event.create_response("Unrecognised unit specified.") return event.create_response("\n".join(output_lines)) # Check if prefix is specified if prefix_name is not None: output_lines = [] # Loop through groups, getting prefixes for each group for prefix_group_obj in repo.prefix_group_list: prefix_obj = prefix_group_obj.get_prefix_by_name( prefix_name) or prefix_group_obj.get_prefix_by_abbr( prefix_name) # If prefix exists by that name, add the string format to output list if prefix_obj is not None: output_lines.append( self.output_prefix_as_string(prefix_obj)) if len(output_lines) == 0: return event.create_response("Unrecognised prefix specified.") return event.create_response("\n".join(output_lines)) # Nothing was specified, return info on the repo. return event.create_response(self.output_repo_as_string(repo))
def test_multiple_simple_args(): p = InputParser("blah blah arg1=val1 arg2=val2 arg3=val3") assert p.remaining_text == "blah blah" assert p.args_dict["arg1"] == "val1" assert p.args_dict["arg2"] == "val2" assert p.args_dict["arg3"] == "val3"
def test_unstripped_input(): p = InputParser(" blah blah ") assert p.remaining_text == "blah blah"
def test_remaining_text_start_and_end(): p = InputParser("blah blah arg1=val1 arg2=val2 hey") assert p.remaining_text == "blah blah hey" assert p.args_dict["arg1"] == "val1" assert p.args_dict["arg2"] == "val2"
def test_unquoted_args_unquoted_values(): p = InputParser("yo base unit=hello world") assert p.remaining_text == "yo base world" assert p.args_dict["unit"] == "hello"
def test_no_args(): p = InputParser("blah blah") assert p.remaining_text == "blah blah" assert len(p.args_dict) == 0
def test_quoted_args_quoted_values(): p = InputParser("yo 'base unit'=\"hello world\"") assert p.remaining_text == "yo" assert p.args_dict["base unit"] == "hello world"
def run(self, event): # Load convert repo function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function ) # type: Convert repo = convert_function_obj.convert_repo # Parse input parsed = InputParser(event.command_args) # Check if a type is specified type_name = parsed.get_arg_by_names(self.NAMES_TYPE) # Clean type setting from the line to just get the name to remove input_name = parsed.remaining_text # Find unit if type_name is not None: type_obj = repo.get_type_by_name(type_name) if type_obj is None: return event.create_response("This conversion type is not recognised.") input_unit = type_obj.get_unit_by_name(input_name) if input_unit is None: return event.create_response( "This unit name is not recognised for that unit type." ) else: input_unit_list = [] for type_obj in repo.type_list: input_unit = type_obj.get_unit_by_name(input_name) if input_unit is not None: input_unit_list.append(input_unit) # Check if results are 0 if len(input_unit_list) == 0: return event.create_response( "No unit by that name is found in any type." ) # Check if results are >=2 if len(input_unit_list) >= 2: unit_outputs = [] for input_unit in input_unit_list: unit_outputs.append( "{} (type={})".format( input_unit.name_list[0], input_unit.type.name ) ) return event.create_response( "There is more than one unit matching this name: {}".format( ", ".join(unit_outputs) ) ) input_unit = input_unit_list[0] # Ensure it is not a base unit for its type if input_unit == input_unit.type.base_unit: return event.create_response( "You cannot remove the base unit for a unit type." ) # Remove unit input_unit_name = input_unit.name_list[0] input_unit.type.remove_unit(input_unit) # Done return event.create_response( 'Removed unit "{}" from conversion repository.'.format(input_unit_name) )
def run(self, event): # Load repository function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function) # type: Convert repo = convert_function_obj.convert_repo # Parse input parsed = InputParser(event.command_args) # Check for type= type_name = parsed.get_arg_by_names(self.NAMES_TYPE) # Check for unit= unit_name = parsed.get_arg_by_names(self.NAMES_UNIT) # Check for abbr= abbr_name = parsed.get_arg_by_names(self.NAMES_ABBR) # If unit= or abbr= then remaining is the other one. if unit_name is None and abbr_name is not None: unit_name = parsed.remaining_text if abbr_name is None and unit_name is not None: abbr_name = parsed.remaining_text # Get unit list if type_name is None: unit_list = repo.get_full_unit_list() else: type_obj = repo.get_type_by_name(type_name) if type_obj is None: return event.create_response("Unrecognised type.") unit_list = type_obj.get_full_unit_list() # If no unit=, try splitting the line to find where the old name ends and new name begins if unit_name is None and abbr_name is None: # Check at least 2 words are given line_split = parsed.remaining_text.split() if len(line_split) <= 1: return event.create_response( "You must specify both a unit name and an abbreviation to add." ) # Scan remaining text for split pairs = parsed.split_remaining_into_two( lambda x, y: any([u.has_name(x) for u in unit_list])) # If not exactly 1 split, return an error if len(pairs) != 1: return event.create_response( "Could not parse where unit name ends and abbreviation begins. " "Please specify with unit=<name>") # Handle the returned pair unit_name = pairs[0][0] abbr_name = pairs[0][1] # Get the unit object from the name input_unit_list = [] for unit_obj in unit_list: if unit_obj.has_name(unit_name): input_unit_list.append(unit_obj) # If 0 units found, throw error if len(input_unit_list) == 0: return event.create_response("No unit found by that name.") # If 2+ units found, throw error if len(input_unit_list) >= 2: return event.create_response( "Unit name is too ambiguous, please specify with unit=<name> and type=<name>." ) unit_obj = input_unit_list[0] # If abbreviation name is empty, throw error if len(abbr_name) == 0: return event.create_response("Abbreviation name cannot be blank.") # Add the new name unit_obj.add_abbr(abbr_name) # Save repo repo.save_json() # Output message return event.create_response('Added "{}" as a new abbreviation for ' 'the "{}" unit.'.format( abbr_name, unit_obj.name_list[0]))
def run(self, event): # Load repository function_dispatcher = event.server.hallo.function_dispatcher convert_function = function_dispatcher.get_function_by_name("convert") convert_function_obj = function_dispatcher.get_function_object( convert_function) # type: Convert repo = convert_function_obj.convert_repo # Parse input parsed = InputParser(event.command_args) # Check for type= type_name = parsed.get_arg_by_names(self.NAMES_TYPE) # Check for unit= unit_name = parsed.get_arg_by_names(self.NAMES_UNIT) # Check for prefixgroup= prefix_group_name = parsed.get_arg_by_names(self.NAMES_PREFIX_GROUP) # clean up the line input_name = parsed.remaining_text # If unit name xor prefix group is none, use input name if unit_name is None and prefix_group_name is not None: unit_name = input_name if prefix_group_name is None and unit_name is not None: prefix_group_name = input_name # Get unit list if type_name is None: unit_list = repo.get_full_unit_list() else: type_obj = repo.get_type_by_name(type_name) if type_obj is None: return event.create_response("Unrecognised type specified.") unit_list = type_obj.get_full_unit_list() # Get parse remaining text into unit name and prefix group name if unit_name is None and prefix_group_name is None: # Check at least 2 words are given line_split = parsed.remaining_text.split() if len(line_split) <= 1: return event.create_response( "You must specify both a unit name and a prefix group to set." ) # Scan remaining text for split def is_unit_name_valid(name): return any([u.has_name(name) for u in unit_list]) def is_prefix_group_valid(name): return (name.lower() == "none" or repo.get_prefix_group_by_name(name) is not None) pairs = parsed.split_remaining_into_two( lambda x, y: is_unit_name_valid(x) and is_prefix_group_valid(y )) # If not exactly 1 split, return an error if len(pairs) != 1: return event.create_response( "Could not parse where unit name ends and prefix group begins. " "Please specify with unit=<name> prefix_group=<name>") # Handle the returned pair unit_name = pairs[0][0] prefix_group_name = pairs[0][1] if prefix_group_name.lower() == "none": prefix_group = None else: prefix_group = repo.get_prefix_group_by_name(prefix_group_name) else: prefix_group = repo.get_prefix_group_by_name(prefix_group_name) if prefix_group is None and prefix_group_name.lower() != "none": return event.create_response("Prefix group not recognised.") # Get unit object from name input_unit_list = [] for unit_obj in unit_list: if unit_obj.has_name(unit_name): input_unit_list.append(unit_obj) # If 0 units found, throw error if len(input_unit_list) == 0: return event.create_response("No unit found by that name.") # If 2+ units found, throw error if len(input_unit_list) >= 2: return event.create_response( "Unit name is too ambiguous, please specify with unit= and type= ." ) unit_obj = input_unit_list[0] # Set the prefix group unit_obj.valid_prefix_group = prefix_group # Save repo repo.save_json() # Output message if prefix_group is None: prefix_group_name = "none" else: prefix_group_name = prefix_group.name return event.create_response('Set "{}" as the prefix group for ' 'the "{}" unit.'.format( prefix_group_name, unit_obj.name_list[0]))