Merge pull request #2009 from MycroftAI/bugfix/voc-match

Fix behavior of MycroftSkills.voc_match()
pull/2017/head
Åke 2019-02-24 15:50:25 +01:00 committed by GitHub
commit faf29d1fef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 21 deletions

View File

@ -26,6 +26,7 @@ from datetime import datetime, timedelta
import abc import abc
import re import re
from itertools import chain
from adapt.intent import Intent, IntentBuilder from adapt.intent import Intent, IntentBuilder
from os.path import join, abspath, dirname, basename, exists from os.path import join, abspath, dirname, basename, exists
from threading import Event, Timer from threading import Event, Timer
@ -41,7 +42,8 @@ from mycroft.messagebus.message import Message
from mycroft.metrics import report_metric, report_timing, Stopwatch from mycroft.metrics import report_metric, report_timing, Stopwatch
from mycroft.skills.settings import SkillSettings from mycroft.skills.settings import SkillSettings
from mycroft.skills.skill_data import (load_vocabulary, load_regex, to_alnum, from mycroft.skills.skill_data import (load_vocabulary, load_regex, to_alnum,
munge_regex, munge_intent_parser) munge_regex, munge_intent_parser,
read_vocab_file)
from mycroft.util import camel_case_split, resolve_resource_file from mycroft.util import camel_case_split, resolve_resource_file
from mycroft.util.log import LOG from mycroft.util.log import LOG
@ -716,15 +718,15 @@ class MycroftSkill:
if not voc or not exists(voc): if not voc or not exists(voc):
raise FileNotFoundError( raise FileNotFoundError(
'Could not find {}.voc file'.format(voc_filename)) 'Could not find {}.voc file'.format(voc_filename))
# load vocab and flatten into a simple list
with open(voc) as f: vocab = list(chain(*read_vocab_file(voc)))
self.voc_match_cache[cache_key] = f.read().splitlines() self.voc_match_cache[cache_key] = vocab
if utt:
# Check for match # Check for matches against complete words
if utt and any(i.strip() in utt return any([re.match(r'.*\b' + i + r'\b.*', utt)
for i in self.voc_match_cache[cache_key]): for i in self.voc_match_cache[cache_key]])
return True else:
return False return False
def report_metric(self, name, data): def report_metric(self, name, data):
""" Report a skill metric to the Mycroft servers """ Report a skill metric to the Mycroft servers

View File

@ -25,6 +25,28 @@ from mycroft.messagebus.message import Message
from mycroft.util.format import expand_options from mycroft.util.format import expand_options
def read_vocab_file(path):
""" Read voc file.
This reads a .voc file, stripping out empty lines comments and expand
parentheses. It retruns each line as a list of all expanded
alternatives.
Arguments:
path (str): path to vocab file.
Returns:
List of Lists of strings.
"""
vocab = []
with open(path, 'r', encoding='utf8') as voc_file:
for line in voc_file.readlines():
if line.startswith('#') or line.strip() == '':
continue
vocab.append(expand_options(line))
return vocab
def load_vocab_from_file(path, vocab_type, bus): def load_vocab_from_file(path, vocab_type, bus):
"""Load Mycroft vocabulary from file """Load Mycroft vocabulary from file
The vocab is sent to the intent handler using the message bus The vocab is sent to the intent handler using the message bus
@ -36,19 +58,15 @@ def load_vocab_from_file(path, vocab_type, bus):
skill_id(str): skill id skill_id(str): skill id
""" """
if path.endswith('.voc'): if path.endswith('.voc'):
with open(path, 'r', encoding='utf8') as voc_file: for parts in read_vocab_file(path):
for line in voc_file.readlines(): entity = parts[0]
if line.startswith("#"): bus.emit(Message("register_vocab", {
continue 'start': entity, 'end': vocab_type
parts = expand_options(line) }))
entity = parts[0] for alias in parts[1:]:
bus.emit(Message("register_vocab", { bus.emit(Message("register_vocab", {
'start': entity, 'end': vocab_type 'start': alias, 'end': vocab_type, 'alias_of': entity
})) }))
for alias in parts[1:]:
bus.emit(Message("register_vocab", {
'start': alias, 'end': vocab_type, 'alias_of': entity
}))
def load_regex_from_file(path, bus, skill_id): def load_regex_from_file(path, bus, skill_id):

View File

@ -0,0 +1 @@
(turn off|switch off)

View File

@ -0,0 +1,3 @@
turn off
switch off

View File

@ -484,6 +484,28 @@ class MycroftSkillTest(unittest.TestCase):
# handler # handler
self.assertTrue('A:sched_handler1' not in [e[0] for e in s.events]) self.assertTrue('A:sched_handler1' not in [e[0] for e in s.events])
def test_voc_match(self):
s = SimpleSkill1()
s.root_dir = abspath(dirname(__file__))
self.assertTrue(s.voc_match("turn off the lights", "turn_off_test"))
self.assertTrue(s.voc_match("would you please turn off the lights",
"turn_off_test"))
self.assertFalse(s.voc_match("return office", "turn_off_test"))
self.assertTrue(s.voc_match("switch off the lights", "turn_off_test"))
self.assertFalse(s.voc_match("", "turn_off_test"))
self.assertFalse(s.voc_match("switch", "turn_off_test"))
self.assertFalse(s.voc_match("My hovercraft is full of eels",
"turn_off_test"))
self.assertTrue(s.voc_match("turn off the lights", "turn_off2_test"))
self.assertFalse(s.voc_match("return office", "turn_off2_test"))
self.assertTrue(s.voc_match("switch off the lights", "turn_off2_test"))
self.assertFalse(s.voc_match("", "turn_off_test"))
self.assertFalse(s.voc_match("switch", "turn_off_test"))
self.assertFalse(s.voc_match("My hovercraft is full of eels",
"turn_off_test"))
class _TestSkill(MycroftSkill): class _TestSkill(MycroftSkill):
def __init__(self): def __init__(self):