diff --git a/rivescript/sorting.py b/rivescript/sorting.py index 44eeb40..bbdf6f4 100644 --- a/rivescript/sorting.py +++ b/rivescript/sorting.py @@ -9,7 +9,7 @@ from .regexp import RE from . import utils import re -from operator import itemgetter, attrgetter +from operator import attrgetter import sys @@ -31,7 +31,7 @@ class TriggerObj(object): star: Number of wildcards (``*``), excluding alphabetical wildcards, and numeric wildcards pound: Number of numeric wildcards (``#``) under: Number of alphabetical wildcards (``_``) - option: Number of optional tags ("[man]" in "hey [man]") + option: Number of optional tags ("[man]" in "hey [man]"), assume that the template is properly formatted """ def __init__(self, pattern, index, weight, inherit = sys.maxsize): @@ -39,12 +39,22 @@ def __init__(self, pattern, index, weight, inherit = sys.maxsize): self.index = index # For rearrange items in the sorted array self.weight = - weight # Negative weight to place i.e. -100 < 0 self.inherit = inherit # Low inherit takes precedence i.e. 0 < 1 - self.wordcount = - utils.word_count(pattern) # Length -2 < -1 - self.len = -len(self.alphabet) # Length -10 < -5 - self.star = self.alphabet.count('*') # Number of wildcards 0 < 1 - self.pound = self.alphabet.count('#') # Number of numeric wildcards 0 < 1 - self.under = self.alphabet.count('_') # Number of alphabetical wildcards 0 < 1 - self.option = self.alphabet.count('[') # Assume that the template is properly formatted 0 < 1 + self.wordcount = - utils.word_count(pattern) # Length -2 < -1. Use `utils` for counting choice of wildcards + self.len = -len(self.alphabet) # Length -10 < -5 + self.star = self.alphabet.count('*') # Number of wildcards 0 < 1 + self.pound = self.alphabet.count('#') # Number of numeric wildcards 0 < 1 + self.under = self.alphabet.count('_') # Number of alphabetical wildcards 0 < 1 + self.option = self.alphabet.count('[') + self.alphabet.count('(') # Number of option 0 < 1 + + if self.star > 0: + if self.pound == 0 & self.under == 0 & self.option == 0: # Place single star last in the rank + self.pound = sys.maxsize + self.under = sys.maxsize + self.option = sys.maxsize + if self.wordcount == 0: # The special case for single star "*", or a grey case "* *" + self.wordcount = sys.maxsize # Make sure template "hello *" > "*" + # Without any words number of stars does not matter, they all mean match any. + self.star = sys.maxsize # Make sure "*" is last in the list, "* love *" > "*" def sort_trigger_set(triggers, exclude_previous=True, say=None): diff --git a/tests/test_sorting.py b/tests/test_sorting.py new file mode 100644 index 0000000..b094f12 --- /dev/null +++ b/tests/test_sorting.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals, absolute_import + +from rivescript.exceptions import RS_ERR_MATCH +from .config import RiveScriptTestCase + +class SortingTriggersTest(RiveScriptTestCase): + """Topic tests.""" + + def test_sorting_triggers(self): + self.new(""" + + * you * + - 1 + + + * are * + - 2 + + + * how are you * + - 3 + + + * hallo ween * + - 4 + + + (hi|hii) + - 5 + + + hello + - 6 + + + hey [man] + - 7 + + + good morning + - 8 + + + * + - 9 + + + hel lo + - 10 + + + hi * + - 11 + + + hi [*] + - 12 + """) + + sorted_triggers = {trig[0]:position for position, trig in enumerate(self.rs._brain.master._sorted["topics"]['random'])} + + # 1) Atomic is first matched. + self.assertLess(sorted_triggers['hello'],sorted_triggers['hey [man]']) + + # 2) Sorted by number of words + self.assertLess(sorted_triggers['hel lo'],sorted_triggers['hello']) + self.assertLess(sorted_triggers['* hallo ween *'],sorted_triggers['* are *']) + + # 3) Sorted by length by characters + self.assertLess(sorted_triggers['good morning'],sorted_triggers['hel lo']) + + # 4) Sorted by alphabetical order + self.assertLess(sorted_triggers['* are *'],sorted_triggers['* you *']) + + # 5) Sorted by number of wildcard triggers + self.assertLess(sorted_triggers['hi *'],sorted_triggers['* you *']) + + # 6) The `super catch all` (only single star `*`) should be least priority + self.assertEqual(sorted_triggers['*'],max(sorted_triggers.values())) + self.assertLess(sorted_triggers['hi [*]'],sorted_triggers['*']) # another check but will be covered by max check above + +