summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/__init__.py0
-rwxr-xr-xclient/diagnose.py108
-rw-r--r--client/g2p.py17
-rwxr-xr-xclient/main.py68
-rwxr-xr-xclient/start.sh2
-rw-r--r--client/test.py40
-rw-r--r--client/vocabcompiler.py69
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)