diff options
Diffstat (limited to 'client')
| -rw-r--r-- | client/__init__.py | 0 | ||||
| -rwxr-xr-x | client/diagnose.py | 108 | ||||
| -rw-r--r-- | client/g2p.py | 17 | ||||
| -rwxr-xr-x | client/main.py | 68 | ||||
| -rwxr-xr-x | client/start.sh | 2 | ||||
| -rw-r--r-- | client/test.py | 40 | ||||
| -rw-r--r-- | client/vocabcompiler.py | 69 |
7 files changed, 226 insertions, 78 deletions
diff --git a/client/__init__.py b/client/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/client/__init__.py diff --git a/client/diagnose.py b/client/diagnose.py new file mode 100755 index 0000000..415eb6e --- /dev/null +++ b/client/diagnose.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python2 +import time +import re +import socket +import os +from subprocess import check_output, call + + +class Diagnostics: + + """ + Set of diagnostics to be run for determining the health of the + host running Jasper + + To add new checks, add a boolean returning method with a name that starts + with `check_` + """ + @classmethod + def jasper_modules_path(cls): + return os.path.abspath('../../') + + @classmethod + def check_network_connection(cls): + try: + # see if we can resolve the host name -- tells us if there is + # a DNS listening + host = socket.gethostbyname("www.google.com") + # connect to the host -- tells us if the host is actually + # reachable + socket.create_connection((host, 80), 2) + except Exception: + return False + else: + return True + + @classmethod + def check_phonetisaurus_dictionary_file(cls): + return os.path.isfile(os.path.join(cls.jasper_modules_path(), "phonetisaurus/g014b2b.fst")) + + @classmethod + def check_phonetisaurus_program(cls): + return call(['which', 'phonetisaurus-g2p']) == 0 + + @classmethod + def info_git_revision(cls): + return check_output(['git', 'rev-parse', 'HEAD']) + + +class DiagnosticRunner: + + """ + Performs a series of checks against the system, printing the results to the + console and also saving them to diagnostic.log + """ + + def __init__(self, diagnostics): + self.diagnostics = diagnostics + + def run(self): + self.initialize_log() + self.perform_checks() + + def perform_checks(self): + self.failed_checks = 0 + for check in self.select_methods('check'): + self.do_check(check) + for info in self.select_methods('info'): + self.get_info(info) + if self.failed_checks == 0: + self.log("All checks passed\n") + else: + self.log("%d checks failed\n" % self.failed_checks) + + def select_methods(self, prefix): + def is_match(method_name): + return callable(getattr(self.diagnostics, method_name)) and re.match(r"\A" + prefix + "_", method_name) + + return [method_name for method_name in dir(self.diagnostics) if is_match(method_name)] + + def initialize_log(self): + self.output = open('diagnostic.log', 'w') + self.log("Starting jasper diagnostic\n") + self.log(time.strftime("%c") + "\n") + + def log(self, msg): + print msg, + self.output.write(msg) + + def get_info(self, info_name): + message = info_name.replace("info_", "").replace("_", " ") + info_method = getattr(self.diagnostics, info_name) + info = info_method() + self.log("%s: %s" % (message, info)) + + def do_check(self, check_name): + message = check_name.replace("check_", "").replace("_", " ") + check = getattr(self.diagnostics, check_name) + self.log("Checking %s... " % message) + if check(): + self.log("OK") + else: + self.failed_checks += 1 + self.log("FAILED") + self.log("\n") + + +if __name__ == '__main__': + DiagnosticRunner(Diagnostics).run() diff --git a/client/g2p.py b/client/g2p.py index d22445a..3225c73 100644 --- a/client/g2p.py +++ b/client/g2p.py @@ -1,9 +1,9 @@ # -*- coding: utf-8-*- import os +import tempfile import subprocess import re -TEMP_FILENAME = "g2ptemp" PHONE_MATCH = re.compile(r'<s> (.*) </s>') PHONETISAURUS_PATH = os.environ['JASPER_HOME'] + "/phonetisaurus" @@ -25,12 +25,12 @@ def translateWord(word): def translateWords(words): full_text = '\n'.join(words) - f = open(TEMP_FILENAME, "wb") - f.write(full_text) - f.flush() + with tempfile.NamedTemporaryFile(suffix='.g2p', delete=False) as f: + temp_filename = f.name + f.write(full_text) - output = translateFile(TEMP_FILENAME) - os.remove(TEMP_FILENAME) + output = translateFile(temp_filename) + os.remove(temp_filename) return output @@ -43,9 +43,8 @@ def translateFile(input_filename, output_filename=None): if output_filename: out = '\n'.join(out) - f = open(output_filename, "wb") - f.write(out) - f.close() + with open(output_filename, "wb") as f: + f.write(out) return None diff --git a/client/main.py b/client/main.py index 7167c95..6ec7137 100755 --- a/client/main.py +++ b/client/main.py @@ -1,65 +1,11 @@ #!/usr/bin/env python2 # -*- coding: utf-8-*- - +# This file exists for backwards compatibility with older versions of jasper. +# It might be removed in future versions. import os import sys -import shutil - -# Change CWD to $JASPER_HOME/jasper/client -jasper_home = os.getenv("JASPER_HOME") -if not jasper_home or not os.path.exists(jasper_home): - print("Error: $JASPER_HOME is not set.") - sys.exit(0) - -os.chdir(os.path.join(jasper_home, "jasper", "client")) - -old_client = os.path.abspath(os.path.join(os.pardir, "old_client")) -if os.path.exists(old_client): - shutil.rmtree(old_client) - -import yaml -import sys -import speaker -import stt -from conversation import Conversation - - -def isLocal(): - return len(sys.argv) > 1 and sys.argv[1] == "--local" - -if isLocal(): - from local_mic import Mic -else: - from mic import Mic - -if __name__ == "__main__": - - print "===========================================================" - print " JASPER The Talking Computer " - print " Copyright 2013 Shubhro Saha & Charlie Marsh " - print "===========================================================" - - profile = yaml.safe_load(open("profile.yml", "r")) - - try: - api_key = profile['keys']['GOOGLE_SPEECH'] - except KeyError: - api_key = None - - try: - stt_engine_type = profile['stt_engine'] - except KeyError: - print "stt_engine not specified in profile, defaulting to PocketSphinx" - stt_engine_type = "sphinx" - - mic = Mic(speaker.newSpeaker(), stt.PocketSphinxSTT(), - stt.newSTTEngine(stt_engine_type, api_key=api_key)) - - addendum = "" - if 'first_name' in profile: - addendum = ", %s" % profile["first_name"] - mic.say("How can I be of service%s?" % addendum) - - conversation = Conversation("JASPER", mic, profile) - - conversation.handleForever() +import runpy +script_path = os.path.join(os.path.dirname(__file__), os.pardir, "jasper.py") +sys.path.remove(os.path.dirname(__file__)) +sys.path.insert(0, os.path.dirname(script_path)) +runpy.run_path(script_path, run_name="__main__") diff --git a/client/start.sh b/client/start.sh index cde8d8d..240a7f7 100755 --- a/client/start.sh +++ b/client/start.sh @@ -1,4 +1,4 @@ #!/bin/bash # This file exists for backwards compatibility with older versions of Jasper. # It might be removed in future versions. -"${0%/*}/main.py" +"${0%/*}/../jasper.py" diff --git a/client/test.py b/client/test.py index a8f12be..8925e66 100644 --- a/client/test.py +++ b/client/test.py @@ -11,8 +11,10 @@ import argparse from mock import patch from urllib2 import URLError, urlopen import test_mic +import vocabcompiler import g2p import brain +from diagnose import Diagnostics DEFAULT_PROFILE = { 'prefers_email': False, @@ -21,14 +23,38 @@ DEFAULT_PROFILE = { 'phone_number': '012344321' } - def activeInternet(): - try: - urlopen('http://www.google.com', timeout=1) - return True - except URLError: - return False + return Diagnostics.check_network_connection() + +class UnorderedList(list): + + def __eq__(self, other): + return sorted(self) == sorted(other) + +class TestVocabCompiler(unittest.TestCase): + + def testWordExtraction(self): + sentences = "temp_sentences.txt" + dictionary = "temp_dictionary.dic" + languagemodel = "temp_languagemodel.lm" + + words = [ + 'HACKER', 'LIFE', 'FACEBOOK', 'THIRD', 'NO', 'JOKE', + 'NOTIFICATION', 'MEANING', 'TIME', 'TODAY', 'SECOND', + 'BIRTHDAY', 'KNOCK KNOCK', 'INBOX', 'OF', 'NEWS', 'YES', + 'TOMORROW', 'EMAIL', 'WEATHER', 'FIRST', 'MUSIC', 'SPOTIFY' + ] + + with patch.object(g2p, 'translateWords') as translateWords: + with patch.object(vocabcompiler, 'text2lm') as text2lm: + vocabcompiler.compile(sentences, dictionary, languagemodel) + # 'words' is appended with ['MUSIC', 'SPOTIFY'] + # so must be > 2 to have received WORDS from modules + translateWords.assert_called_once_with(UnorderedList(words)) + self.assertTrue(text2lm.called) + os.remove(sentences) + os.remove(dictionary) class TestMic(unittest.TestCase): @@ -209,7 +235,7 @@ if __name__ == '__main__': help='runs a subset of the tests (only requires Python dependencies)') args = parser.parse_args() - test_cases = [TestBrain, TestModules] + test_cases = [TestBrain, TestModules, TestVocabCompiler] if not args.light: test_cases.append(TestG2P) test_cases.append(TestMic) diff --git a/client/vocabcompiler.py b/client/vocabcompiler.py new file mode 100644 index 0000000..1b4d94c --- /dev/null +++ b/client/vocabcompiler.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8-*- +""" + Iterates over all the WORDS variables in the modules and creates a dictionary for the client. +""" + +import os +import sys +import glob + +mod_path = os.path.abspath('modules/') + +sys.path.append(mod_path) + +import g2p + + +def text2lm(in_filename, out_filename): + """Wrapper around the language model compilation tools""" + def text2idngram(in_filename, out_filename): + cmd = "text2idngram -vocab %s < %s -idngram temp.idngram" % (out_filename, + in_filename) + os.system(cmd) + + def idngram2lm(in_filename, out_filename): + cmd = "idngram2lm -idngram temp.idngram -vocab %s -arpa %s" % ( + in_filename, out_filename) + os.system(cmd) + + text2idngram(in_filename, in_filename) + idngram2lm(in_filename, out_filename) + + +def compile(sentences, dictionary, languagemodel): + """ + Gets the words and creates the dictionary + """ + + m = [os.path.basename(f)[:-3] + for f in glob.glob(os.path.dirname("../client/modules/") + "/*.py")] + + words = [] + for module_name in m: + try: + exec("import %s" % module_name) + eval("words.extend(%s.WORDS)" % module_name) + except: + pass # module probably doesn't have the property + + words = list(set(words)) + + # for spotify module + words.extend(["MUSIC", "SPOTIFY"]) + + # create the dictionary + pronounced = g2p.translateWords(words) + zipped = zip(words, pronounced) + lines = ["%s %s" % (x, y) for x, y in zipped] + + with open(dictionary, "w") as f: + f.write("\n".join(lines) + "\n") + + # create the language model + with open(sentences, "w") as f: + f.write("\n".join(words) + "\n") + f.write("<s> \n </s> \n") + f.close() + + # make language model + text2lm(sentences, languagemodel) |
