class TemplateGraphListProcessingTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" def test_first(self): template = ET.fromstring(""" <template> <first>one two three four</first> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "one") def test_rest(self): template = ET.fromstring(""" <template> <rest>one two three four</rest> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "two three four")
class TemplateGraphThinkTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_think(self): template = ET.fromstring(""" <template> <think>XYZ</think> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "")
class TemplateGraphTopicTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_topicstar_index_as_attrib_full(self): template = ET.fromstring(""" <template> <topicstar index="1"></topicstar> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.children[0].to_string(), "TOPICSTAR Index=1") self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "*")
class TemplateGraphSraixTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_sraix_template_params_as_attribs(self): template = ET.fromstring(""" <template> <sraix host="hostname" botid="testbot" hint="test query" apikey="1234567890" service="/ask"> Ask this question </sraix> </template> """) ast = self.parser.parse_template_expression(template) def test_sraix_template_params_as_children(self): template = ET.fromstring(""" <template> <sraix> <host>hostname</host> <botid>testbot</botid> <hint>test query</hint> <apikey>1234567890</apikey> <service>/ask</service> Ask this question </sraix> </template> """) ast = self.parser.parse_template_expression(template)
class TemplateGraphBasicTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_template_no_content(self): template = ET.fromstring(""" <template> </template> """) with self.assertRaises(ParserException): ast = self.parser.parse_template_expression(template) def test_base_template(self): template = ET.fromstring(""" <template>HELLO WORLD</template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(2, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateWordNode) self.assertIsInstance(ast.children[1], TemplateWordNode)
class TemplateGraphBasicRequestResponseTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_response_index_as_attrib_full(self): template = ET.fromstring(""" <template> <response index="9"></response> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) def test_request_index_as_attrib_full(self): template = ET.fromstring(""" <template> <request index="8"></request> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast)
class TemplateGraphSystemTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_system_timeout_as_attrib_full(self): template = ET.fromstring(""" <template> <system timeout="1000">echo "Hello World"</system> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "Hello World") def test_size(self): template = ET.fromstring(""" <template> <size /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "0")
class TemplateGraphLearnTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph(AIMLParser()) self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_eval_simple(self): template = ET.fromstring(""" <template> <eval>sometext</eval> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) eval_node = ast.children[0] self.assertIsNotNone(eval_node) self.assertIsInstance(eval_node, TemplateEvalNode) self.assertIsNotNone(eval_node.children) self.assertEqual(len(eval_node.children), 1) self.assertIsInstance(eval_node.children[0], TemplateWordNode) self.assertEqual(eval_node.resolve(self.test_bot, self.test_clientid), "sometext") def test_learn_simple(self): template = ET.fromstring(""" <template> <learn> <category> <pattern>HELLO <eval>WORLD</eval> THERE</pattern> <template>HIYA</template> </category> </learn> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) learn_node = ast.children[0] self.assertIsNotNone(learn_node) self.assertIsInstance(learn_node, TemplateLearnNode) self.assertIsNotNone(learn_node._pattern) self.assertIsInstance(learn_node._pattern, ET.Element) self.assertIsNotNone(learn_node._topic) self.assertIsInstance(learn_node._topic, ET.Element) self.assertIsNotNone(learn_node._that) self.assertIsInstance(learn_node._that, ET.Element) self.assertIsNotNone(learn_node._template) self.assertIsInstance(learn_node._template, TemplateNode) resolved = learn_node.resolve(self.test_bot, self.test_clientid) self.assertEqual(resolved, "") response = self.test_bot.ask_question(self.test_clientid, "HELLO WORLD THERE") self.assertEqual("HIYA", response) def test_learnf_simple(self): template = ET.fromstring(""" <template> <learnf> <category> <pattern>HELLO <eval>WORLD</eval> THERE</pattern> <template>HIYA</template> </category> </learnf> </template> """) self.test_bot.brain._configuration._aiml_files = BrainFileConfiguration( "/tmp", ".aiml", False) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) learn_node = ast.children[0] self.assertIsNotNone(learn_node) self.assertIsInstance(learn_node, TemplateLearnNode) self.assertIsNotNone(learn_node._pattern) self.assertIsInstance(learn_node._pattern, ET.Element) self.assertIsNotNone(learn_node._topic) self.assertIsInstance(learn_node._topic, ET.Element) self.assertIsNotNone(learn_node._that) self.assertIsInstance(learn_node._that, ET.Element) self.assertIsNotNone(learn_node._template) self.assertIsInstance(learn_node._template, TemplateNode) resolved = learn_node.resolve(self.test_bot, self.test_clientid) self.assertEqual(resolved, "") response = self.test_bot.ask_question(self.test_clientid, "HELLO WORLD THERE") self.assertEqual("HIYA", response)
class AIMLParser(object): def __init__(self, supress_warnings=False, stop_on_invalid=False): self._supress_warnings = supress_warnings self.stop_on_invalid = stop_on_invalid self.pattern_parser = PatternGraph() self.template_parser = TemplateGraph(self) self._aiml_loader = AIMLLoader(self) self._num_categories = 0 self._duplicates = None self._errors = None @property def supress_warnings(self): return self._supress_warnings @property def num_categories(self): return self._num_categories def create_debug_storage(self, brain_configuration): if brain_configuration.aiml_files.errors is not None: self._errors = [] if brain_configuration.aiml_files.duplicates is not None: self._duplicates = [] def save_debug_files(self, brain_configuration): if brain_configuration.aiml_files.errors is not None: logging.info("Saving aiml errors to file [%s]" % brain_configuration.aiml_files.errors) try: with open(brain_configuration.aiml_files.errors, "w+") as errors_file: for error in self._errors: errors_file.write(error) except Exception as e: logging.exception(e) if brain_configuration.aiml_files.duplicates is not None: logging.info("Saving aiml duplicates to file [%s]" % brain_configuration.aiml_files.duplicates) try: with open(brain_configuration.aiml_files.duplicates, "w+") as duplicates_file: for duplicate in self._duplicates: duplicates_file.write(duplicate) except Exception as e: logging.exception(e) def display_debug_info(self, brain_configuration): if self._errors is not None: logging.info( "Found a total of %d errors in your grammrs, check out [%s] for details" % (len(self._errors), brain_configuration.aiml_files.errors)) if self._duplicates is not None: logging.info( "Found a total of %d duplicate patterns in your grammrs, check out [%s] for details" % (len(self._duplicates), brain_configuration.aiml_files.duplicates)) def load_files_from_directory(self, brain_configuration): start = datetime.datetime.now() aimls_loaded = self._aiml_loader.load_dir_contents( brain_configuration.aiml_files.files, brain_configuration.aiml_files.directories, brain_configuration.aiml_files.extension) stop = datetime.datetime.now() diff = stop - start logging.info("Total processing time %f.2 secs" % diff.total_seconds()) logging.info("Loaded a total of %d aiml files with %d categories" % (len(aimls_loaded), self.num_categories)) if diff.total_seconds() > 0: logging.info("Thats approx %f aiml files per sec" % (len(aimls_loaded) / diff.total_seconds())) def load_single_file(self, brain_configuration): start = datetime.datetime.now() self._aiml_loader.load_single_file_contents( brain_configuration.aiml_files.file) stop = datetime.datetime.now() diff = stop - start logging.info("Total processing time %f.2 secs" % diff.total_seconds()) logging.info("Loaded a single aiml file with %d categories" % (self.num_categories)) def load_aiml(self, brain_configuration: BrainConfiguration): self._supress_warnings = brain_configuration.supress_warnings if brain_configuration.aiml_files is not None: self.create_debug_storage(brain_configuration) if brain_configuration.aiml_files.files is not None: self.load_files_from_directory(brain_configuration) elif brain_configuration.aiml_files.file is not None: self.load_single_file(brain_configuration) else: logging.info( "No AIML files or file defined in configuration to load") self.save_debug_files(brain_configuration) self.display_debug_info(brain_configuration) else: logging.info( "No AIML files or file defined in configuration to load") if brain_configuration.dump_to_file is not None: logging.debug("Dumping AIML Graph as tree to [%s]" % brain_configuration.dump_to_file) self.pattern_parser.dump_to_file(brain_configuration.dump_to_file) def parse_from_file(self, filename): """ Parse an AIML file and return all the cateogeries found in the file :param filename: Name of file to parse :return list of categories parsed from file: """ logging.info("Loading aiml file: " + filename) try: #print("Parsing raw XML Tree") tree = ET.parse(filename, parser=LineNumberingParser()) #print("Parsed...") aiml = tree.getroot() if aiml is None or aiml.tag != 'aiml': raise ParserException("Error, root tag is not <aiml>", filename=filename) else: #print("Parsing loaded AIML tree") #start = datetime.datetime.now() self.parse_aiml(aiml, filename) #stop = datetime.datetime.now() #diff = stop - start #print("Parsed in %f sec"%(diff.total_seconds())) except Exception as e: logging.exception(e) logging.error( "Failed to load contents of AIML file from [%s] - [%s]" % (filename, e)) def parse_from_text(self, text): """ Parse an AIML text version of an aiml file and return all the cateogeries found in the file :param text: Fully validated AIML snippet :return list of categories parsed from file: """ aiml = ET.fromstring(text) if aiml is None or aiml.tag != 'aiml': raise ParserException("Error, root tag is not <aiml>", filename="text") else: self.parse_aiml(aiml, "text") ######################################################################################### # # <?xml version = "1.0" encoding = "UTF-8"?> # <aiml> # <category> # : # </category> # <topic> # <category> # : # </category> # </topic> # </aiml> # def handle_aiml_duplicate(self, dupe_excep, filename): if self._duplicates is not None: dupe_excep.filename = filename msg = dupe_excep.format_message() self._duplicates.append(msg + "\n") logging.error(msg) def handle_aiml_error(self, parser_excep, filename): parser_excep.filename = filename msg = parser_excep.format_message() logging.error(msg) if self._errors is not None: self._errors.append(msg + "\n") if self.stop_on_invalid is True: raise parser_excep def parse_aiml(self, aiml_xml, filename): self.parse_version(aiml_xml) categories_found = False for expression in aiml_xml: if expression.tag == 'topic': try: self.parse_topic(expression) categories_found = True except DuplicateGrammarException as dupe_excep: self.handle_aiml_duplicate(dupe_excep, filename) except ParserException as parser_excep: self.handle_aiml_error(parser_excep, filename) elif expression.tag == 'category': try: self.parse_category(expression) categories_found = True except DuplicateGrammarException as dupe_excep: self.handle_aiml_duplicate(dupe_excep, filename) except ParserException as parser_excep: self.handle_aiml_error(parser_excep, filename) else: raise ParserException("Error, unknown top level tag, %s" % expression.tag, xml_element=expression) if categories_found is False: logging.warning("no categories in aiml file") if self.stop_on_invalid is True: raise ParserException("Error, no categories in aiml file", filename=filename) ######################################################################################### # # AIML_VERSION ::== 0.9 | 1.0 | 1.1 | 2.0 # def parse_version(self, aiml): if 'version' in aiml.attrib: version = aiml.attrib['version'] if version not in ['0.9', '1.0', '1.1', '2.0']: if self._supress_warnings is False: logging.warning( "Version number not a supported version: %s", version) else: if self._supress_warnings is False: logging.warning("No version info, defaulting to 2.0") version = "2.0" return version ######################################################################################### # # TOPIC_EXPRESSION:: == <topic name = "PATTERN_EXPRESSION" > (CATEGORY_EXPRESSION) + < / topic > # # PATTERN_EXPRESSION:: == WORD | PRIORITY_WORD | WILDCARD | SET_STATEMENT | PATTERN_SIDE_BOT_PROPERTY_EXPRESSION # PATTERN_EXPRESSION:: == PATTERN_EXPRESSION PATTERN_EXPRESSION # # This means both topic and that can also be a set of words, stars, hash, sets and bots # # CATEGORY_EXPRESSION:: == <category> # <pattern> PATTERN_EXPRESSION </pattern> # (<that> PATTERN_EXPRESSION </that>) # (<topic> PATTERN_EXPRESSION </topic>) # < template > TEMPLATE_EXPRESSION < / template > # </category> def parse_topic(self, topic_element): if 'name' in topic_element.attrib: name = topic_element.attrib['name'] if name is None or len(name) == 0: raise ParserException("Topic name empty or null", xml_element=topic_element) xml = "<topic>%s</topic>" % name logging.info("Topic attrib converted to %s", xml) topic_pattern = ET.fromstring(xml) else: raise ParserException("Error, missing name attribute for topic", xml_element=topic_element) category_found = False for child in topic_element: if child.tag == 'category': self.parse_category(child, topic_pattern) category_found = True else: raise ParserException("Error unknown child node of topic, %s" % child.tag, xml_element=topic_element) if category_found is False: raise ParserException("Error, no categories in topic", xml_element=topic_element) def find_topic(self, category_xml, topic_element=None): topics = category_xml.findall('topic') if topic_element is not None: if len(topics) > 0: raise ParserException( "Error, topic exists in category AND as parent node", xml_element=category_xml) else: if len(topics) > 1: raise ParserException( "Error, multiple <topic> nodes found in category", xml_element=category_xml) elif len(topics) == 1: topic_element = topics[0] else: topic_element = ET.fromstring("<topic>*</topic>") return topic_element def find_that(self, category_xml): thats = category_xml.findall('that') if len(thats) > 1: raise ParserException( "Error, multiple <that> nodes found in category", xml_element=category_xml) elif len(thats) == 1: that_element = thats[0] else: that_element = ET.fromstring("<that>*</that>") return that_element def get_template(self, category_xml): templates = category_xml.findall('template') if len(templates) == 0: raise ParserException("Error, no template node found in category", xml_element=category_xml) elif len(templates) > 1: raise ParserException( "Error, multiple <template> nodes found in category", xml_element=category_xml) else: return self.template_parser.parse_template_expression(templates[0]) def get_pattern(self, category_xml): patterns = category_xml.findall('pattern') if len(patterns) == 0: raise ParserException("Error, no pattern node found in category", xml_element=category_xml) elif len(patterns) > 1: raise ParserException( "Error, multiple <pattern> nodes found in category", xml_element=category_xml) else: return patterns[0] def parse_category(self, category_xml, topic_element=None, add_to_graph=True): topic_element = self.find_topic(category_xml, topic_element) that_element = self.find_that(category_xml) template_graph_root = self.get_template(category_xml) pattern = self.get_pattern(category_xml) if add_to_graph is True: self.pattern_parser.add_pattern_to_graph(pattern, topic_element, that_element, template_graph_root) self._num_categories += 1 return (pattern, topic_element, that_element, template_graph_root) def match_sentence(self, bot, clientid, pattern_sentence, topic_pattern, that_pattern): topic_sentence = Sentence(topic_pattern) that_sentence = Sentence(that_pattern) logging.debug( "AIML Parser matching sentence [%s], topic=[%s], that=[%s] ", pattern_sentence.text(), topic_pattern, that_pattern) sentence = Sentence() sentence.append_sentence(pattern_sentence) sentence.append_word('__TOPIC__') sentence.append_sentence(topic_sentence) sentence.append_word('__THAT__') sentence.append_sentence(that_sentence) logging.debug("Matching [%s]" % sentence.words_from_current_pos(0)) context = MatchContext() template = self.pattern_parser._root_node.match( bot, clientid, context, sentence) if template is not None: context._template_node = template context.list_matches() # Save the matched context for the associated sentence pattern_sentence.matched_context = context return context return None
class TemplateGraphRandomTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_random_template_no_li(self): template = ET.fromstring(""" <template> <random> </random> </template> """) with self.assertRaises(ParserException): ast = self.parser.parse_template_expression(template) def test_random_template(self): template = ET.fromstring(""" <template> <random> <li>1</li> <li>2</li> <li>3</li> </random> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertIsNotNone(ast.children[0]) self.assertIsInstance(ast.children[0], TemplateRandomNode) self.assertEqual(3, len(ast.children[0].children)) self.assertIsInstance(ast.children[0].children[0], TemplateNode) self.assertIsInstance(ast.children[0].children[1], TemplateNode) self.assertIsInstance(ast.children[0].children[2], TemplateNode) selection = ast.children[0].resolve(self.test_bot, self.test_clientid) self.assertIsNotNone(selection) self.assertIn(selection, ['1', '2', '3']) def test_random_nested_template(self): template = ET.fromstring(""" <template> <random> <li> <random> <li>Say something</li> <li>Say the other</li> </random> </li> <li> <random> <li>Hello world!</li> <li>Goodbye cruel world</li> </random> </li> </random> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertIsNotNone(ast.children[0]) self.assertIsInstance(ast.children[0], TemplateRandomNode) self.assertEqual(2, len(ast.children[0].children)) self.assertIsInstance(ast.children[0].children[0], TemplateNode) self.assertEqual(1, len(ast.children[0].children[0].children)) self.assertIsInstance(ast.children[0].children[0].children[0], TemplateRandomNode) self.assertEqual(2, len(ast.children[0].children[0].children[0].children)) self.assertIsInstance(ast.children[0].children[1], TemplateNode) self.assertEqual(1, len(ast.children[0].children[1].children)) self.assertIsInstance(ast.children[0].children[1].children[0], TemplateRandomNode) self.assertEqual(2, len(ast.children[0].children[1].children[0].children)) selection = ast.children[0].resolve(self.test_bot, self.test_clientid) self.assertIsNotNone(selection) self.assertIn(selection, ['Say something', 'Say the other', 'Hello world!', 'Goodbye cruel world'])
class TemplateGraphTransformsTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_lowercase(self): template = ET.fromstring(""" <template> <lowercase>This is a Sentence</lowercase> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "this is a sentence") def test_uppercase(self): template = ET.fromstring(""" <template> <uppercase>This is a Sentence</uppercase> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "THIS IS A SENTENCE") def test_formal(self): template = ET.fromstring(""" <template> <formal>This is a Sentence</formal> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "This Is A Sentence") def test_sentence(self): template = ET.fromstring(""" <template> <sentence>This is a Sentence</sentence> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "This is a sentence") def test_normalize(self): template = ET.fromstring(""" <template> <normalize>XYZ</normalize> </template> """) ast = self.parser.parse_template_expression(template) def test_denormalize(self): template = ET.fromstring(""" <template> <denormalize>XYZ</denormalize> </template> """) ast = self.parser.parse_template_expression(template) def test_person(self): template = ET.fromstring(""" <template> <person>XYZ</person> </template> """) ast = self.parser.parse_template_expression(template) def test_person2(self): template = ET.fromstring(""" <template> <person2>XYZ</person2> </template> """) ast = self.parser.parse_template_expression(template) def test_gender(self): template = ET.fromstring(""" <template> <gender>XYZ</gender> </template> """) ast = self.parser.parse_template_expression(template) def test_sr(self): template = ET.fromstring(""" <template> <sr>XYZ</sr> </template> """) ast = self.parser.parse_template_expression(template) def test_id(self): template = ET.fromstring(""" <template> <id /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, "clientid"), "clientid") def test_vocabulary(self): template = ET.fromstring(""" <template> <vocabulary>XYZ</vocabulary> </template> """) ast = self.parser.parse_template_expression(template) def test_program(self): template = ET.fromstring(""" <template> <program>XYZ</program> </template> """) ast = self.parser.parse_template_expression(template) def test_explode(self): template = ET.fromstring(""" <template> <explode>XYZ</explode> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "X Y Z") def test_implode(self): template = ET.fromstring(""" <template> <implode>X Y Z</implode> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(None, None), "XYZ")
class AIMLParser(object): def __init__(self, brain=None): self._brain = brain self.pattern_parser = PatternGraph(aiml_parser=self) self.template_parser = TemplateGraph(aiml_parser=self) self._aiml_loader = AIMLLoader(self) self._num_categories = 0 self._duplicates = None self._errors = None @property def brain(self): return self._brain @property def num_categories(self): return self._num_categories def create_debug_storage(self, brain_configuration): if brain_configuration.files.aiml_files.errors is not None: self._errors = [] if brain_configuration.files.aiml_files.duplicates is not None: self._duplicates = [] def save_debug_files(self, brain_configuration): if brain_configuration.files.aiml_files.errors is not None: if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Saving aiml errors to file [%s]", brain_configuration.files.aiml_files.errors) try: with open(brain_configuration.files.aiml_files.errors, "w+") as errors_file: for error in self._errors: errors_file.write(error) except Exception as excep: logging.exception(excep) if brain_configuration.files.aiml_files.duplicates is not None: if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Saving aiml duplicates to file [%s]", brain_configuration.files.aiml_files.duplicates) try: with open(brain_configuration.files.aiml_files.duplicates, "w+") as duplicates_file: for duplicate in self._duplicates: duplicates_file.write(duplicate) except Exception as excep: logging.exception(excep) def display_debug_info(self, brain_configuration): if self._errors is not None: if logging.getLogger().isEnabledFor(logging.INFO): logging.info( "Found a total of %d errors in your grammrs, check out [%s] for details", len(self._errors), brain_configuration.files.aiml_files.errors) if self._duplicates is not None: if logging.getLogger().isEnabledFor(logging.INFO): logging.info( "Found a total of %d duplicate patterns in your grammrs, check out [%s] for details", len(self._duplicates), brain_configuration.files.aiml_files.duplicates) def load_files_from_directory(self, brain_configuration): start = datetime.datetime.now() total_aimls_loaded = 0 for file in brain_configuration.files.aiml_files.files: aimls_loaded = self._aiml_loader.load_dir_contents( file, brain_configuration.files.aiml_files.directories, brain_configuration.files.aiml_files.extension) total_aimls_loaded = len(aimls_loaded) stop = datetime.datetime.now() diff = stop - start if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Total processing time %.6f secs", diff.total_seconds()) logging.info("Loaded a total of %d aiml files with %d categories", total_aimls_loaded, self.num_categories) if diff.total_seconds() > 0: if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Thats approx %f aiml files per sec", total_aimls_loaded / diff.total_seconds()) def load_single_file(self, brain_configuration): start = datetime.datetime.now() self._aiml_loader.load_single_file_contents( brain_configuration.files.aiml_files.file) stop = datetime.datetime.now() diff = stop - start if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Total processing time %.6f secs", diff.total_seconds()) logging.info("Loaded a single aiml file with %d categories", self.num_categories) def load_aiml(self, brain_configuration: BrainConfiguration): if brain_configuration.files.aiml_files is not None: self.create_debug_storage(brain_configuration) if brain_configuration.files.aiml_files.has_multiple_files(): self.load_files_from_directory(brain_configuration) elif brain_configuration.files.aiml_files.has_single_file(): self.load_single_file(brain_configuration) else: if logging.getLogger().isEnabledFor(logging.INFO): logging.info( "No AIML files or file defined in configuration to load" ) self.save_debug_files(brain_configuration) self.display_debug_info(brain_configuration) else: if logging.getLogger().isEnabledFor(logging.INFO): logging.info( "No AIML files or file defined in configuration to load") def tag_and_namespace_from_text(self, text): # If there is a namespace, then it looks something like # {http://alicebot.org/2001/AIML}aiml pattern = re.compile("^{.*}.*$") if pattern.match(text) is None: # If that pattern does not exist, assume that the text is the tag name return text, None # Otherwise, extract namespace and tag name match = re.compile("^({.*})(.*)$") groupings = match.match(text) if groupings is not None: namespace = groupings.group(1).strip() tag_name = groupings.group(2).strip() return tag_name, namespace return None, None def tag_from_text(self, text): tag, _ = self.tag_and_namespace_from_text(text) return tag def check_aiml_tag(self, aiml, filename=None): # Null check just to be sure if aiml is None: raise ParserException("Error, null root tag", filename=filename) tag_name, namespace = self.tag_and_namespace_from_text(aiml.tag) # Then if check is just <aiml>, thats OK if tag_name != 'aiml': raise ParserException("Error, root tag is not <aiml>", filename=filename) return tag_name, namespace def parse_from_file(self, filename): """ Parse an AIML file and return all the cateogeries found in the file :param filename: Name of file to parse :return list of categories parsed from file: """ if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Loading aiml file: " + filename) try: tree = ET.parse(filename, parser=LineNumberingParser()) aiml = tree.getroot() _, namespace = self.check_aiml_tag(aiml, filename=filename) start = datetime.datetime.now() num_categories = self.parse_aiml(aiml, namespace, filename) stop = datetime.datetime.now() diff = stop - start if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Processed %s with %d categories in %f.2 secs", filename, num_categories, diff.total_seconds()) except Exception as excep: logging.exception(excep) if logging.getLogger().isEnabledFor(logging.ERROR): logging.error( "Failed to load contents of AIML file from [%s] - [%s]", filename, excep) def parse_from_text(self, text): """ Parse an AIML text version of an aiml file and return all the cateogeries found in the file :param text: Fully validated AIML snippet :return list of categories parsed from file: """ aiml = ET.fromstring(text) _, namespace = self.check_aiml_tag(aiml) self.parse_aiml(aiml, namespace) ######################################################################################### # # <?xml version = "1.0" encoding = "UTF-8"?> # <aiml> # <category> # : # </category> # <topic> # <category> # : # </category> # </topic> # </aiml> # def handle_aiml_duplicate(self, dupe_excep, filename, expression): if self._duplicates is not None: dupe_excep.filename = filename msg = dupe_excep.format_message() if hasattr(expression, "_start_line_number"): msg += " start line [" + str( expression._start_line_number) + "] " if hasattr(expression, "_end_line_number"): msg += " end line [" + str(expression._end_line_number) + "] " msg += "\n" if logging.getLogger().isEnabledFor(logging.ERROR): logging.error(msg) if self._duplicates is not None: self._duplicates.append(msg) def handle_aiml_error(self, parser_excep, filename, expression): parser_excep.filename = filename msg = parser_excep.format_message() if hasattr(expression, "_start_line_number"): msg += " start line [" + str(expression._start_line_number) + "] " if hasattr(expression, "_end_line_number"): msg += " end line [" + str(expression._end_line_number) + "] " msg += "\n" if logging.getLogger().isEnabledFor(logging.ERROR): logging.error(msg) if self._errors is not None: self._errors.append(msg) def parse_aiml(self, aiml_xml, namespace, filename=None): self.parse_version(aiml_xml) categories_found = False num_category = 0 for expression in aiml_xml: tag_name, namespace = self.tag_and_namespace_from_text( expression.tag) if tag_name == 'topic': try: num_topic_categories = self.parse_topic( expression, namespace) num_category += num_topic_categories categories_found = True except DuplicateGrammarException as dupe_excep: self.handle_aiml_duplicate(dupe_excep, filename, expression) except ParserException as parser_excep: self.handle_aiml_error(parser_excep, filename, expression) elif tag_name == 'category': try: self.parse_category(expression, namespace) categories_found = True num_category += 1 except DuplicateGrammarException as dupe_excep: self.handle_aiml_duplicate(dupe_excep, filename, expression) except ParserException as parser_excep: self.handle_aiml_error(parser_excep, filename, expression) else: raise ParserException("Error, unknown top level tag, %s" % expression.tag, xml_element=expression) if categories_found is False: if logging.getLogger().isEnabledFor(logging.WARNING): logging.warning("no categories in aiml file") return num_category ######################################################################################### # # AIML_VERSION ::== 0.9 | 1.0 | 1.1 | 2.0 # def parse_version(self, aiml): if 'version' in aiml.attrib: version = aiml.attrib['version'] if version not in ['0.9', '1.0', '1.1', '2.0']: if logging.getLogger().isEnabledFor(logging.WARNING): logging.warning( "Version number not a supported version: %s", version) else: if logging.getLogger().isEnabledFor(logging.WARNING): logging.warning("No version info, defaulting to 2.0") version = "2.0" return version ######################################################################################### # # TOPIC_EXPRESSION:: == <topic name = "PATTERN_EXPRESSION" > (CATEGORY_EXPRESSION) + < / topic > # # PATTERN_EXPRESSION:: == WORD | PRIORITY_WORD | WILDCARD | SET_STATEMENT | PATTERN_SIDE_BOT_PROPERTY_EXPRESSION # PATTERN_EXPRESSION:: == PATTERN_EXPRESSION PATTERN_EXPRESSION # # This means both topic and that can also be a set of words, stars, hash, sets and bots # # CATEGORY_EXPRESSION:: == <category> # <pattern> PATTERN_EXPRESSION </pattern> # (<that> PATTERN_EXPRESSION </that>) # (<topic> PATTERN_EXPRESSION </topic>) # < template > TEMPLATE_EXPRESSION < / template > # </category> def parse_topic(self, topic_element, namespace): if 'name' in topic_element.attrib: name = topic_element.attrib['name'] if name is None or not name: raise ParserException("Topic name empty or null", xml_element=topic_element) xml = "<topic>%s</topic>" % name if logging.getLogger().isEnabledFor(logging.INFO): logging.info("Topic attrib converted to %s", xml) topic_pattern = ET.fromstring(xml) else: raise ParserException("Error, missing name attribute for topic", xml_element=topic_element) category_found = False num_category = 0 for child in topic_element: tag_name, _ = self.tag_and_namespace_from_text(child.tag) if tag_name == 'category': self.parse_category(child, namespace, topic_pattern) category_found = True num_category += 1 else: raise ParserException("Error unknown child node of topic, %s" % child.tag, xml_element=topic_element) if category_found is False: raise ParserException("Error, no categories in topic", xml_element=topic_element) return num_category def find_all(self, element, name, namespace): if namespace is not None: search = '%s%s' % (namespace, name) return element.findall(search) return element.findall(name) def find_topic(self, category_xml, namespace, topic_element=None): topics = self.find_all(category_xml, "topic", namespace) if topic_element is not None: if topics: raise ParserException( "Error, topic exists in category AND as parent node", xml_element=category_xml) else: if len(topics) > 1: raise ParserException( "Error, multiple <topic> nodes found in category", xml_element=category_xml) elif len(topics) == 1: topic_element = topics[0] else: topic_element = ET.fromstring("<topic>*</topic>") return topic_element def find_that(self, category_xml, namespace): thats = self.find_all(category_xml, "that", namespace) if len(thats) > 1: raise ParserException( "Error, multiple <that> nodes found in category", xml_element=category_xml) elif len(thats) == 1: that_element = thats[0] else: that_element = ET.fromstring("<that>*</that>") return that_element def get_template(self, category_xml, namespace): templates = self.find_all(category_xml, "template", namespace) if not templates: raise ParserException("Error, no template node found in category", xml_element=category_xml) elif len(templates) > 1: raise ParserException( "Error, multiple <template> nodes found in category", xml_element=category_xml) else: return self.template_parser.parse_template_expression(templates[0]) def get_pattern(self, category_xml, namespace): patterns = self.find_all(category_xml, "pattern", namespace) if not patterns: raise ParserException("Error, no pattern node found in category", xml_element=category_xml) elif len(patterns) > 1: raise ParserException( "Error, multiple <pattern> nodes found in category", xml_element=category_xml) else: return patterns[0] def parse_category(self, category_xml, namespace, topic_element=None, add_to_graph=True): topic_element = self.find_topic(category_xml, namespace, topic_element) that_element = self.find_that(category_xml, namespace) template_graph_root = self.get_template(category_xml, namespace) pattern = self.get_pattern(category_xml, namespace) if add_to_graph is True: self.pattern_parser.add_pattern_to_graph(pattern, topic_element, that_element, template_graph_root) self._num_categories += 1 return (pattern, topic_element, that_element, template_graph_root) def match_sentence(self, bot, clientid, pattern_sentence, topic_pattern, that_pattern): topic_sentence = Sentence(topic_pattern) that_sentence = Sentence(that_pattern) if logging.getLogger().isEnabledFor(logging.DEBUG): logging.debug( "AIML Parser matching sentence [%s], topic=[%s], that=[%s] ", pattern_sentence.text(), topic_pattern, that_pattern) sentence = Sentence() sentence.append_sentence(pattern_sentence) sentence.append_word('__TOPIC__') sentence.append_sentence(topic_sentence) sentence.append_word('__THAT__') sentence.append_sentence(that_sentence) if logging.getLogger().isEnabledFor(logging.DEBUG): logging.debug("Matching [%s]", sentence.words_from_current_pos(0)) context = MatchContext( max_search_depth=bot.configuration.max_search_depth, max_search_timeout=bot.configuration.max_search_timeout) template = self.pattern_parser._root_node.match( bot, clientid, context, sentence) if template is not None: context._template_node = template context.list_matches() # Save the matched context for the associated sentence pattern_sentence.matched_context = context return context return None
class TemplateGraphConditionTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_condition_template_no_attribs(self): template = ET.fromstring(""" <template> <condition> </condition> </template> """) with self.assertRaises(ParserException): self.parser.parse_template_expression(template) def test_condition_template_type1_variant1_name(self): template = ET.fromstring(""" <template> <condition name="aname" value="avalue">X <random><li>1</li><li>2</li></random> Y</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 3) def test_condition_template_type1_variant1_var(self): template = ET.fromstring(""" <template> <condition var="aname" value="avalue">X <random><li>1</li><li>2</li></random> Y</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertTrue(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 3) def test_condition_template_type1_variant2_name(self): template = ET.fromstring(""" <template> <condition name="aname"><value>avalue</value>X</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 1) def test_condition_template_type1_variant2_var(self): template = ET.fromstring(""" <template> <condition var="aname"><value>avalue</value>X</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertTrue(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 1) def test_condition_template_type1_variant3_name(self): template = ET.fromstring(""" <template> <condition value="avalue"><name>aname</name>X</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 1) def test_condition_template_type1_variant3_var(self): template = ET.fromstring(""" <template> <condition value="avalue"><var>aname</var>X</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertTrue(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 1) def test_condition_template_type1_variant4_name(self): template = ET.fromstring(""" <template> <condition><name>aname</name><value>avalue</value>X</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 1) def test_condition_template_type1_variant4_var(self): template = ET.fromstring(""" <template> <condition><var>aname</var><value>avalue</value>X</condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType1ConditionNode) self.assertTrue(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(template_node.value, "avalue") self.assertEqual(len(template_node.children), 1) def test_condition_template_type2_variant1_name(self): template = ET.fromstring(""" <template> <condition> <name>aname</name> <li value="a">A</li> <li value="b">B</li> <li><value>c</value>C</li> <li>D</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType2ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(len(template_node.children), 4) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "A") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "B") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "C") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "D") def test_condition_template_type2_variant1_name_with_loop(self): template = ET.fromstring(""" <template> <condition> <name>aname</name> <li value="a">A <loop /></li> <li value="b">B</li> <li><value>c</value>C</li> <li>D</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType2ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(len(template_node.children), 4) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertTrue(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "A") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "B") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "C") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "D") def test_condition_template_type2_variant1_var(self): template = ET.fromstring(""" <template> <condition> <var>aname</var> <li value="a">A</li> <li value="b">B</li> <li><value>c</value>C</li> <li>D</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType2ConditionNode) self.assertTrue(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(len(template_node.children), 4) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "A") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "B") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "C") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "D") def test_condition_template_type2_variant2_name(self): template = ET.fromstring(""" <template> <condition name="aname"> <li value="a">A</li> <li value="b">B</li> <li><value>c</value>C</li> <li>D</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType2ConditionNode) self.assertFalse(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(len(template_node.children), 4) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "A") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "B") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "C") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "D") def test_condition_template_type2_variant2_var(self): template = ET.fromstring(""" <template> <condition var="aname"> <li value="a">A</li> <li value="b">B</li> <li><value>c</value>C</li> <li>D</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType2ConditionNode) self.assertTrue(template_node.local) self.assertEqual(template_node.name, "aname") self.assertEqual(len(template_node.children), 4) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "A") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "B") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "C") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "D") def test_condition_template_type3_variant1_name(self): template = ET.fromstring(""" <template> <condition> <li name='name1' value="a">Val1</li> <li value="b"><name>name2</name>Val2</li> <li name="name3"><value>c</value>Val3</li> <li><name>name4</name><value>d</value>Val4</li> <li>Val5</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType3ConditionNode) self.assertEqual(len(template_node.children), 5) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name1") self.assertIsNotNone(node.value) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val1") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name2") self.assertIsNotNone(node.value) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val2") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name3") self.assertIsNotNone(node.value) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val3") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertEqual(node.name, "name4") self.assertIsNotNone(node.value) self.assertEqual(node.value, "d") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val4") node = template_node.children[4] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val5") def test_condition_template_type3_variant1_name_with_loop(self): template = ET.fromstring(""" <template> <condition> <li name='name1' value="a">Val1 <loop /></li> <li value="b"><name>name2</name>Val2</li> <li name="name3"><value>c</value>Val3</li> <li><name>name4</name><value>d</value>Val4</li> <li>Val5</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType3ConditionNode) self.assertEqual(len(template_node.children), 5) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertTrue(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name1") self.assertIsNotNone(node.value) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val1") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name2") self.assertIsNotNone(node.value) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val2") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name3") self.assertIsNotNone(node.value) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val3") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertEqual(node.name, "name4") self.assertIsNotNone(node.value) self.assertEqual(node.value, "d") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val4") node = template_node.children[4] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val5") def test_condition_template_type3_variant1_var(self): template = ET.fromstring(""" <template> <condition> <li var='name1' value="a">Val1</li> <li value="b"><var>name2</var>Val2</li> <li var="name3"><value>c</value>Val3</li> <li><var>name4</var><value>d</value>Val4</li> <li>Val5</li> </condition> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) template_node = ast.children[0] self.assertIsNotNone(template_node) self.assertIsInstance(template_node, TemplateType3ConditionNode) self.assertEqual(len(template_node.children), 5) node = template_node.children[0] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name1") self.assertIsNotNone(node.value) self.assertEqual(node.value, "a") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val1") node = template_node.children[1] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name2") self.assertIsNotNone(node.value) self.assertEqual(node.value, "b") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val2") node = template_node.children[2] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name3") self.assertIsNotNone(node.value) self.assertEqual(node.value, "c") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val3") node = template_node.children[3] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertTrue(node.local) self.assertFalse(node.loop) self.assertIsNotNone(node.name) self.assertEqual(node.name, "name4") self.assertIsNotNone(node.value) self.assertEqual(node.value, "d") self.assertFalse(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val4") node = template_node.children[4] self.assertIsInstance(node, TemplateConditionListItemNode) self.assertFalse(node.local) self.assertFalse(node.loop) self.assertIsNone(node.name) self.assertIsNone(node.value) self.assertTrue(node.is_default()) self.assertEqual(len(node.children), 1) self.assertEqual(node.children[0].resolve(None, None), "Val5")
class AIMLParser(object): def __init__(self, supress_warnings=False, stop_on_invalid=False): self._supress_warnings = supress_warnings self.stop_on_invalid = stop_on_invalid self.pattern_parser = PatternGraph() self.template_parser = TemplateGraph(self) self._filename = "Unknown" self._version = "Unknown" self._aiml_loader = AIMLLoader(self) self._num_categories = 0 @property def supress_warnings(self): return self._supress_warnings @property def num_categories(self): return self._num_categories def load_aiml(self, brain_configuration: BrainConfiguration): self._supress_warnings = brain_configuration.supress_warnings if brain_configuration.aiml_files is not None: aimls_loaded = self._aiml_loader.load_dir_contents( brain_configuration.aiml_files.files, brain_configuration.aiml_files.directories, brain_configuration.aiml_files.extension) logging.info("Loaded a total of %d aiml files", len(aimls_loaded)) else: logging.info("No AIML files defined in configuration to load") def parse_from_file(self, filename): """ Parse an AIML file and return all the cateogeries found in the file :param filename: Name of file to parse :return list of categories parsed from file: """ self._filename = filename logging.info("Loading aiml file file: " + self._filename) tree = ET.parse(filename, parser=LineNumberingParser()) aiml = tree.getroot() if aiml is None or aiml.tag != 'aiml': raise ParserException("Error, root tag is not <aiml>", filename=filename) else: try: self.parse_aiml(aiml, filename) except ParserException as parser_excep: parser_excep.filename = filename raise parser_excep except ET.ParseError as xmlpe: xmlpe.filename = filename xmlpe.xml_exception = xmlpe def parse_from_text(self, text): """ Parse an AIML text version of an aiml file and return all the cateogeries found in the file :param text: Fully validated AIML snippet :return list of categories parsed from file: """ aiml = ET.fromstring(text) if aiml is None or aiml.tag != 'aiml': ParserException("Error, root tag is not <aiml>", filename="text") else: self.parse_aiml(aiml, "text") ######################################################################################### # # <?xml version = "1.0" encoding = "UTF-8"?> # <aiml> # <category> # : # </category> # <topic> # <category> # : # </category> # </topic> # </aiml> # def parse_aiml(self, aiml_xml, filename): self.parse_version(aiml_xml) categories_found = False for expression in aiml_xml: if expression.tag == 'topic': try: self.parse_topic(expression) categories_found = True except ParserException as parser_excep: parser_excep.filename = filename logging.error(parser_excep.format_message()) if self.stop_on_invalid is True: raise parser_excep elif expression.tag == 'category': try: self.parse_category(expression) categories_found = True except ParserException as parser_excep: parser_excep.filename = filename logging.error(parser_excep.format_message()) if self.stop_on_invalid is True: raise parser_excep else: raise ParserException("Error, unknown top level tag, %s" % expression.tag, xml_element=expression) if categories_found is False: logging.warning("no categories in aiml file") if self.stop_on_invalid is True: raise ParserException("Error, no categories in aiml file", filename=filename) ######################################################################################### # # AIML_VERSION ::== 0.9 | 1.0 | 1.1 | 2.0 # def parse_version(self, aiml): if 'version' in aiml.attrib: self._version = aiml.attrib['version'] if self._version not in ['0.9', '1.0', '1.1', '2.0']: if self._supress_warnings is False: logging.warning( "Version number not a supported version: %s", self._version) else: if self._supress_warnings is False: logging.warning("No version info, defaulting to 2.0") self._version = "2.0" ######################################################################################### # # TOPIC_EXPRESSION:: == <topic name = "PATTERN_EXPRESSION" > (CATEGORY_EXPRESSION) + < / topic > # # PATTERN_EXPRESSION:: == WORD | PRIORITY_WORD | WILDCARD | SET_STATEMENT | PATTERN_SIDE_BOT_PROPERTY_EXPRESSION # PATTERN_EXPRESSION:: == PATTERN_EXPRESSION PATTERN_EXPRESSION # # This means both topic and that can also be a set of words, stars, hash, sets and bots # # CATEGORY_EXPRESSION:: == <category> # <pattern> PATTERN_EXPRESSION </pattern> # (<that> PATTERN_EXPRESSION </that>) # (<topic> PATTERN_EXPRESSION </topic>) # < template > TEMPLATE_EXPRESSION < / template > # </category> def parse_topic(self, topic_element): if 'name' in topic_element.attrib: name = topic_element.attrib['name'] if name is None or len(name) == 0: raise ParserException("Topic name empty or null", xml_element=topic_element) xml = "<topic>%s</topic>" % name logging.info("Topic attrib converted to %s", xml) topic_pattern = ET.fromstring(xml) else: raise ParserException("Error, missing name attribute for topic", xml_element=topic_element) category_found = False for child in topic_element: logging.debug(child.tag) if child.tag == 'category': self.parse_category(child, topic_pattern) category_found = True else: raise ParserException("Error unknown child node of topic, %s" % child.tag, xml_element=topic_element) if category_found is False: raise ParserException("Error, no categories in topic", xml_element=topic_element) def parse_category(self, category_xml, topic_element=None, add_to_graph=True): topics = category_xml.findall('topic') if topic_element is not None: if len(topics) > 0: raise ParserException( "Error, topic exists in category AND as parent node", xml_element=category_xml) else: if len(topics) > 1: raise ParserException( "Error, multiple <topic> nodes found in category", xml_element=category_xml) elif len(topics) == 1: topic_element = topics[0] else: topic_element = ET.fromstring("<topic>*</topic>") thats = category_xml.findall('that') if len(thats) > 1: raise ParserException( "Error, multiple <that> nodes found in category", xml_element=category_xml) elif len(thats) == 1: that_element = thats[0] else: that_element = ET.fromstring("<that>*</that>") templates = category_xml.findall('template') if len(templates) == 0: raise ParserException("Error, no template node found in category", xml_element=category_xml) elif len(templates) > 1: raise ParserException( "Error, multiple <template> nodes found in category", xml_element=category_xml) else: template_graph_root = self.template_parser.parse_template_expression( templates[0]) patterns = category_xml.findall('pattern') if len(patterns) == 0: raise ParserException("Error, no pattern node found in category", xml_element=category_xml) elif len(patterns) > 1: raise ParserException( "Error, multiple <pattern> nodes found in category", xml_element=category_xml) else: if add_to_graph is True: self.pattern_parser.add_pattern_to_graph( patterns[0], topic_element, that_element, template_graph_root) self._num_categories += 1 return (patterns[0], topic_element, that_element, template_graph_root) def match_sentence(self, bot, clientid, pattern_sentence, topic_pattern, that_pattern): topic_sentence = Sentence(topic_pattern) that_sentence = Sentence(that_pattern) logging.debug( "AIML Parser matching sentence [%s], topic=[%s], that=[%s] ", pattern_sentence.text(), topic_pattern, that_pattern) sentence = Sentence() sentence.append_sentence(pattern_sentence) sentence.append_word('__TOPIC__') sentence.append_sentence(topic_sentence) sentence.append_word('__THAT__') sentence.append_sentence(that_sentence) logging.debug("Matching [%s]" % sentence.words_from_current_pos(0)) context = MatchContext() template = self.pattern_parser._root_node.match( bot, clientid, context, sentence) if template is not None: context._template_node = template context.list_matches() # Save the matched context for the associated sentence pattern_sentence.matched_context = context return context return None
class TemplateGraphSetTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_set_template_predicate_as_attrib(self): template = ET.fromstring(""" <template> <set name="somepred">Value1</set> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateSetNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somepred") self.assertFalse(set_node.local) self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "Value1") def test_set_template_multi_word_predicate_as_attrib(self): template = ET.fromstring(""" <template> <set name="somepred other">Value1</set> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateSetNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somepred other") self.assertFalse(set_node.local) self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "Value1") def test_set_template_predicate_nested(self): template = ET.fromstring(""" <template> Some text here <set name="somepred">Value1</set> Some text there </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 7) set_node = ast.children[3] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateSetNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somepred") self.assertFalse(set_node.local) self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "Value1") def test_set_template_local_as_attrib(self): template = ET.fromstring(""" <template> <set var="somevar">Value2</set> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateSetNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somevar") self.assertTrue(set_node.local) self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "Value2") def test_set_template_predicate_as_child(self): template = ET.fromstring(""" <template> <set><name>somepred</name>Value3</set> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateSetNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somepred") self.assertFalse(set_node.local) self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "Value3") def test_set_template_local_as_child(self): template = ET.fromstring(""" <template> <set><var>somepred</var>Value4</set> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateSetNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somepred") self.assertTrue(set_node.local) self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "Value4")
class AIMLParser(object): def __init__(self, supress_warnings=False, stop_on_invalid=False): self._supress_warnings = supress_warnings self.stop_on_invalid = stop_on_invalid self.pattern_parser = PatternGraph() self.template_parser = TemplateGraph(self) self._filename = "Unknown" self._version = "Unknown" self._aiml_loader = AIMLLoader(self) self._num_categories = 0 @property def supress_warnings(self): return self._supress_warnings @property def num_categories(self): return self._num_categories def load_aiml(self, brain_configuration: BrainConfiguration): self._supress_warnings = brain_configuration.supress_warnings if brain_configuration.aiml_files is not None: aimls_loaded = self._aiml_loader.load_dir_contents(brain_configuration.aiml_files.files, brain_configuration.aiml_files.directories, brain_configuration.aiml_files.extension) logging.info("Loaded a total of %d aiml files", len(aimls_loaded)) else: logging.info("No AIML files defined in configuration to load") def parse_from_file(self, filename): """ Parse an AIML file and return all the cateogeries found in the file :param filename: Name of file to parse :return list of categories parsed from file: """ self._filename = filename logging.info("Loading aiml file file: " + self._filename) try: tree = ET.parse(filename, parser=LineNumberingParser()) aiml = tree.getroot() if aiml is None or aiml.tag != 'aiml': raise ParserException("Error, root tag is not <aiml>", filename=filename) else: self.parse_aiml(aiml, filename) except Exception as e: logging.error("Failed to load contents of AIML file from [%s] - [%s]"%(filename, e)) def parse_from_text(self, text): """ Parse an AIML text version of an aiml file and return all the cateogeries found in the file :param text: Fully validated AIML snippet :return list of categories parsed from file: """ aiml = ET.fromstring(text) if aiml is None or aiml.tag != 'aiml': raise ParserException("Error, root tag is not <aiml>", filename="text") else: self.parse_aiml(aiml, "text") ######################################################################################### # # <?xml version = "1.0" encoding = "UTF-8"?> # <aiml> # <category> # : # </category> # <topic> # <category> # : # </category> # </topic> # </aiml> # def parse_aiml(self, aiml_xml, filename): self.parse_version(aiml_xml) categories_found = False for expression in aiml_xml: if expression.tag == 'topic': try: self.parse_topic(expression) categories_found = True except ParserException as parser_excep: parser_excep.filename = filename logging.error(parser_excep.format_message()) if self.stop_on_invalid is True: raise parser_excep elif expression.tag == 'category': try: self.parse_category(expression) categories_found = True except ParserException as parser_excep: parser_excep.filename = filename logging.error(parser_excep.format_message()) if self.stop_on_invalid is True: raise parser_excep else: raise ParserException("Error, unknown top level tag, %s" % expression.tag, xml_element=expression) if categories_found is False: logging.warning("no categories in aiml file") if self.stop_on_invalid is True: raise ParserException("Error, no categories in aiml file", filename=filename) ######################################################################################### # # AIML_VERSION ::== 0.9 | 1.0 | 1.1 | 2.0 # def parse_version(self, aiml): if 'version' in aiml.attrib: self._version = aiml.attrib['version'] if self._version not in ['0.9', '1.0', '1.1', '2.0']: if self._supress_warnings is False: logging.warning("Version number not a supported version: %s", self._version) else: if self._supress_warnings is False: logging.warning("No version info, defaulting to 2.0") self._version = "2.0" ######################################################################################### # # TOPIC_EXPRESSION:: == <topic name = "PATTERN_EXPRESSION" > (CATEGORY_EXPRESSION) + < / topic > # # PATTERN_EXPRESSION:: == WORD | PRIORITY_WORD | WILDCARD | SET_STATEMENT | PATTERN_SIDE_BOT_PROPERTY_EXPRESSION # PATTERN_EXPRESSION:: == PATTERN_EXPRESSION PATTERN_EXPRESSION # # This means both topic and that can also be a set of words, stars, hash, sets and bots # # CATEGORY_EXPRESSION:: == <category> # <pattern> PATTERN_EXPRESSION </pattern> # (<that> PATTERN_EXPRESSION </that>) # (<topic> PATTERN_EXPRESSION </topic>) # < template > TEMPLATE_EXPRESSION < / template > # </category> def parse_topic(self, topic_element): if 'name' in topic_element.attrib: name = topic_element.attrib['name'] if name is None or len(name) == 0: raise ParserException("Topic name empty or null", xml_element=topic_element) xml = "<topic>%s</topic>" % name logging.info("Topic attrib converted to %s", xml) topic_pattern = ET.fromstring(xml) else: raise ParserException("Error, missing name attribute for topic", xml_element=topic_element) category_found = False for child in topic_element: logging.debug(child.tag) if child.tag == 'category': self.parse_category(child, topic_pattern) category_found = True else: raise ParserException("Error unknown child node of topic, %s" % child.tag, xml_element=topic_element) if category_found is False: raise ParserException("Error, no categories in topic", xml_element=topic_element) def parse_category(self, category_xml, topic_element=None, add_to_graph=True): topics = category_xml.findall('topic') if topic_element is not None: if len(topics) > 0: raise ParserException("Error, topic exists in category AND as parent node", xml_element=category_xml) else: if len(topics) > 1: raise ParserException("Error, multiple <topic> nodes found in category", xml_element=category_xml) elif len(topics) == 1: topic_element = topics[0] else: topic_element = ET.fromstring("<topic>*</topic>") thats = category_xml.findall('that') if len(thats) > 1: raise ParserException("Error, multiple <that> nodes found in category", xml_element=category_xml) elif len(thats) == 1: that_element = thats[0] else: that_element = ET.fromstring("<that>*</that>") templates = category_xml.findall('template') if len(templates) == 0: raise ParserException("Error, no template node found in category", xml_element=category_xml) elif len(templates) > 1: raise ParserException("Error, multiple <template> nodes found in category", xml_element=category_xml) else: template_graph_root = self.template_parser.parse_template_expression(templates[0]) patterns = category_xml.findall('pattern') if len(patterns) == 0: raise ParserException("Error, no pattern node found in category", xml_element=category_xml) elif len(patterns) > 1: raise ParserException("Error, multiple <pattern> nodes found in category", xml_element=category_xml) else: if add_to_graph is True: self.pattern_parser.add_pattern_to_graph(patterns[0], topic_element, that_element, template_graph_root) self._num_categories += 1 return (patterns[0], topic_element, that_element, template_graph_root) def match_sentence(self, bot, clientid, pattern_sentence, topic_pattern, that_pattern): topic_sentence = Sentence(topic_pattern) that_sentence = Sentence(that_pattern) logging.debug("AIML Parser matching sentence [%s], topic=[%s], that=[%s] ", pattern_sentence.text(), topic_pattern, that_pattern) sentence = Sentence() sentence.append_sentence(pattern_sentence) sentence.append_word('__TOPIC__') sentence.append_sentence(topic_sentence) sentence.append_word('__THAT__') sentence.append_sentence(that_sentence) logging.debug("Matching [%s]"%sentence.words_from_current_pos(0)) context = MatchContext() template = self.pattern_parser._root_node.match(bot, clientid, context, sentence) if template is not None: context._template_node = template context.list_matches() # Save the matched context for the associated sentence pattern_sentence.matched_context = context return context return None
class TemplateGraphDateTimeTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_date_format_as_attrib(self): template = ET.fromstring(""" <template> <date format="%c" /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.resolve(self.test_bot, self.test_clientid)) def test_date_format_as_attrib_full(self): template = ET.fromstring(""" <template> <date format="%c"></date> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.resolve(self.test_bot, self.test_clientid)) def test_date_format_as_attrib_default(self): template = ET.fromstring(""" <template> <date/> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.resolve(self.test_bot, self.test_clientid)) def test_interval_values_as_attribs(self): template = ET.fromstring(""" <template> <interval> <format>%c</format> <style>days</style> <from>Wed Oct 5 16:35:11 2016</from> <to>Fri Oct 7 16:35:11 2016</to> </interval> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "2")
class TemplateGraphBotTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_extension_as_attrib(self): template = ET.fromstring(""" <template> <extension path="test.parser.template.graph.test_extension.TestExtension"> 1 2 3 </extension> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) ext_node = ast.children[0] self.assertIsNotNone(ext_node) self.assertIsInstance(ext_node, TemplateExtensionNode) self.assertIsNotNone(ext_node._path) self.assertEqual(len(ext_node.children), 3) self.assertEqual("executed", ext_node.resolve(None, None)) def test_extension_as_child(self): template = ET.fromstring(""" <template> <extension> <path>test.parser.template.graph.test_extension.TestExtension</path> 1 2 3 </extension> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) ext_node = ast.children[0] self.assertIsNotNone(ext_node) self.assertIsInstance(ext_node, TemplateExtensionNode) self.assertIsNotNone(ext_node._path) self.assertEqual(len(ext_node.children), 3) self.assertEqual("executed", ext_node.resolve(None, None))
class TemplateGraphSraiTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_srai_template_simple(self): template = ET.fromstring(""" <template> <srai> SRAI this text </srai> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertIsNotNone(ast.children[0]) self.assertIsInstance(ast.children[0], TemplateSRAINode) self.assertIsNotNone(ast.children[0].children) self.assertEqual(3, len(ast.children[0].children)) self.assertIsInstance(ast.children[0].children[0], TemplateWordNode) self.assertIsInstance(ast.children[0].children[1], TemplateWordNode) self.assertIsInstance(ast.children[0].children[2], TemplateWordNode) def test_srai_template_nested(self): template = ET.fromstring(""" <template> <srai> SRAI This and <srai>SRAI that</srai> </srai> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertIsNotNone(ast.children[0]) self.assertIsInstance(ast.children[0], TemplateSRAINode) self.assertIsNotNone(ast.children[0].children) self.assertEqual(4, len(ast.children[0].children)) self.assertIsInstance(ast.children[0].children[0], TemplateWordNode) self.assertIsInstance(ast.children[0].children[1], TemplateWordNode) self.assertIsInstance(ast.children[0].children[2], TemplateWordNode) self.assertIsInstance(ast.children[0].children[3], TemplateSRAINode) self.assertIsNotNone(ast.children[0].children[3].children) self.assertEqual(2, len(ast.children[0].children[3].children)) self.assertIsInstance(ast.children[0].children[3].children[0], TemplateWordNode) self.assertIsInstance(ast.children[0].children[3].children[1], TemplateWordNode)
class TemplateGraphGetTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = ['one', 'two', 'three', 'four', 'five', 'six'] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_get_template_predicate_as_attrib(self): template = ET.fromstring(""" <template> <get name="somepred" /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) get_node = ast.children[0] self.assertIsNotNone(get_node) self.assertIsInstance(get_node, TemplateGetNode) self.assertIsNotNone(get_node.name) self.assertIsInstance(get_node.name, TemplateNode) self.assertEqual(get_node.name.resolve(None, None), "somepred") self.assertFalse(get_node.local) def test_get_template_predicate_as_attrib_mixed(self): template = ET.fromstring(""" <template> Hello <get name="somepred" /> how are you </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 5) get_node = ast.children[1] self.assertIsNotNone(get_node) self.assertIsInstance(get_node, TemplateGetNode) self.assertIsNotNone(get_node.name) self.assertIsInstance(get_node.name, TemplateNode) self.assertEqual(get_node.name.resolve(None, None), "somepred") self.assertFalse(get_node.local) def test_get_template_var_as_attrib(self): template = ET.fromstring(""" <template> <get var="somevar" /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) get_node = ast.children[0] self.assertIsNotNone(get_node) self.assertIsInstance(get_node, TemplateGetNode) self.assertIsNotNone(get_node.name) self.assertIsInstance(get_node.name, TemplateNode) self.assertEqual(get_node.name.resolve(None, None), "somevar") self.assertTrue(get_node.local) def test_get_template_predicate_as_child(self): template = ET.fromstring(""" <template> <get><name>somepred as text</name></get> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) get_node = ast.children[0] self.assertIsNotNone(get_node) self.assertIsInstance(get_node, TemplateGetNode) self.assertIsNotNone(get_node.name) self.assertIsInstance(get_node.name, TemplateNode) self.assertEqual(get_node.name.resolve(None, None), "somepred as text") self.assertFalse(get_node.local) def test_get_template_local_as_child(self): template = ET.fromstring(""" <template> <get><var>somevar</var></get> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) get_node = ast.children[0] self.assertIsNotNone(get_node) self.assertIsInstance(get_node, TemplateGetNode) self.assertIsNotNone(get_node.name) self.assertIsInstance(get_node.name, TemplateNode) self.assertEqual(get_node.name.resolve(None, None), "somevar") self.assertTrue(get_node.local)
class TemplateGraphMapTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_map_name_as_attrib(self): template = ET.fromstring(""" <template> <map name="somemap">sometext</map> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateMapNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somemap") self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "sometext") def test_map_name_as_child(self): template = ET.fromstring(""" <template> <map><name>somemap</name>sometext</map> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast.children) self.assertEqual(len(ast.children), 1) set_node = ast.children[0] self.assertIsNotNone(set_node) self.assertIsInstance(set_node, TemplateMapNode) self.assertIsNotNone(set_node.name) self.assertIsInstance(set_node.name, TemplateNode) self.assertEqual(set_node.name.resolve(None, None), "somemap") self.assertEqual(len(set_node.children), 1) self.assertEqual(set_node.children[0].resolve(None, None), "sometext")
class TemplateGraphStarTests(unittest.TestCase): def setUp(self): self.parser = TemplateGraph() self.assertIsNotNone(self.parser) self.test_brain = None self.test_sentence = Sentence("test sentence") self.test_sentence._stars = [ 'one', 'two', 'three', 'four', 'five', 'six' ] self.test_sentence._thatstars = ["*"] self.test_sentence._topicstars = ["*"] test_config = ClientConfiguration() self.test_bot = Bot(Brain(BrainConfiguration()), config=test_config.bot_configuration) self.test_clientid = "testid" conversation = self.test_bot.get_conversation(self.test_clientid) question = Question.create_from_sentence(self.test_sentence) conversation._questions.append(question) def test_star_no_index_full(self): template = ET.fromstring(""" <template> <star></star> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.children) self.assertEqual(1, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateStarNode) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "one") def test_star_no_index_full_embedded(self): template = ET.fromstring(""" <template> Hello <star></star> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.children) self.assertEqual(2, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateWordNode) self.assertIsInstance(ast.children[1], TemplateStarNode) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "Hello one") def test_star_no_index_short(self): template = ET.fromstring(""" <template> <star /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.children) self.assertEqual(1, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateStarNode) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "one") def test_star_index_as_child(self): template = ET.fromstring(""" <template> <star><index>2</index></star> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.children) self.assertEqual(1, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateStarNode) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "two") def test_star_index_as_attrib_full(self): template = ET.fromstring(""" <template> <star index="3"></star> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsInstance(ast, TemplateNode) self.assertIsNotNone(ast) self.assertIsNotNone(ast.children) self.assertEqual(1, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateStarNode) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "three") def test_star_index_as_attrib_short(self): template = ET.fromstring(""" <template> <star index="4" /> </template> """) ast = self.parser.parse_template_expression(template) self.assertIsNotNone(ast) self.assertIsNotNone(ast.children) self.assertEqual(1, len(ast.children)) self.assertIsInstance(ast.children[0], TemplateStarNode) self.assertEqual(ast.resolve(self.test_bot, self.test_clientid), "four")