summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rwxr-xr-xboot/boot.py91
-rwxr-xr-xboot/boot.sh2
-rwxr-xr-xboot/test.py54
-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.py (renamed from boot/vocabcompiler.py)4
-rwxr-xr-xjasper.py121
12 files changed, 287 insertions, 221 deletions
diff --git a/.travis.yml b/.travis.yml
index 95079e7..09e0aec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,4 +6,3 @@ env:
script:
- "pip install -r client/requirements.txt"
- "cd client && python test.py --light"
- - "cd ../boot && python test.py"
diff --git a/boot/boot.py b/boot/boot.py
index 5291ea2..6ec7137 100755
--- a/boot/boot.py
+++ b/boot/boot.py
@@ -1,88 +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
-
-# Set $JASPER_HOME
-jasper_home = os.getenv("JASPER_HOME")
-if not jasper_home or not os.path.exists(jasper_home):
- if os.path.exists("/home/pi"):
- jasper_home = "/home/pi"
- os.environ["JASPER_HOME"] = jasper_home
- else:
- print("Error: $JASPER_HOME is not set.")
- sys.exit(0)
-
-# Change CWD to $JASPER_HOME/jasper/boot
-os.chdir(os.path.join(os.getenv("JASPER_HOME"), "jasper", "boot"))
-
-# Set $LD_LIBRARY_PATH
-os.environ["LD_LIBRARY_PATH"] = "/usr/local/lib"
-
-# Set $PATH
-path = os.getenv("PATH")
-if path:
- path = os.pathsep.join([path, "/usr/local/lib/"])
-else:
- path = "/usr/local/lib/"
-os.environ["PATH"] = path
-
-import urllib2
-import vocabcompiler
-import traceback
-
-lib_path = os.path.abspath('../client')
-sys.path.append(lib_path)
-
-import speaker as speak
-speaker = speak.newSpeaker()
-
-
-def testConnection():
- try:
- urllib2.urlopen("http://www.google.com").getcode()
- print "CONNECTED TO INTERNET"
-
- except urllib2.URLError:
- print "COULD NOT CONNECT TO NETWORK"
- speaker.say(
- "Warning: I was unable to connect to a network. Parts of the system may not work correctly, depending on your setup.")
-
-
-def fail(message):
- traceback.print_exc()
- speaker.say(message)
-
-
-def configure():
- try:
- print "COMPILING DICTIONARY"
- vocabcompiler.compile(
- "../client/sentences.txt", "../client/dictionary.dic", "../client/languagemodel.lm")
- print "STARTING CLIENT PROGRAM"
- os.system("$JASPER_HOME/jasper/client/start.sh &")
-
- except OSError:
- print "BOOT FAILURE: OSERROR"
- fail(
- "There was a problem starting Jasper. You may be missing the language model and associated files. Please read the documentation to configure your Raspberry Pi.")
-
- except IOError:
- print "BOOT FAILURE: IOERROR"
- fail(
- "There was a problem starting Jasper. You may have set permissions incorrectly on some part of the filesystem. Please read the documentation to configure your Raspberry Pi.")
-
- except:
- print "BOOT FAILURE"
- fail(
- "There was a problem starting Jasper. Please read the documentation to configure your Raspberry Pi.")
-
-if __name__ == "__main__":
- print "==========STARTING JASPER CLIENT=========="
- print "=========================================="
- print "COPYRIGHT 2013 SHUBHRO SAHA, CHARLIE MARSH"
- print "=========================================="
- speaker.say("Hello.... I am Jasper... Please wait one moment.")
- testConnection()
- configure()
+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/boot/boot.sh b/boot/boot.sh
index 9fc8af8..240a7f7 100755
--- a/boot/boot.sh
+++ b/boot/boot.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%/*}/boot.py"
+"${0%/*}/../jasper.py"
diff --git a/boot/test.py b/boot/test.py
deleted file mode 100755
index 638ca77..0000000
--- a/boot/test.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python2
-# -*- coding: utf-8-*-
-import os
-
-if os.environ.get('JASPER_HOME') is None:
- os.environ['JASPER_HOME'] = '/home/pi'
-
-import sys
-import unittest
-from mock import patch
-import vocabcompiler
-
-lib_path = os.path.abspath('../client')
-mod_path = os.path.abspath('../client/modules/')
-
-sys.path.append(lib_path)
-sys.path.append(mod_path)
-
-import g2p
-
-
-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)
-
-if __name__ == '__main__':
- unittest.main()
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/boot/vocabcompiler.py b/client/vocabcompiler.py
index f22da1a..1b4d94c 100644
--- a/boot/vocabcompiler.py
+++ b/client/vocabcompiler.py
@@ -7,10 +7,8 @@ import os
import sys
import glob
-lib_path = os.path.abspath('../client')
-mod_path = os.path.abspath('../client/modules/')
+mod_path = os.path.abspath('modules/')
-sys.path.append(lib_path)
sys.path.append(mod_path)
import g2p
diff --git a/jasper.py b/jasper.py
new file mode 100755
index 0000000..cd51960
--- /dev/null
+++ b/jasper.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python2
+import os
+import sys
+import traceback
+import shutil
+import yaml
+
+# Set $JASPER_HOME
+jasper_home = os.getenv("JASPER_HOME")
+if not jasper_home or not os.path.exists(jasper_home):
+ if os.path.exists("/home/pi"):
+ jasper_home = "/home/pi"
+ os.environ["JASPER_HOME"] = jasper_home
+ else:
+ print("Error: $JASPER_HOME is not set.")
+ sys.exit(0)
+
+from client.diagnose import Diagnostics
+from client import vocabcompiler, stt
+from client import speaker as speak
+from client.conversation import Conversation
+if len(sys.argv) > 1 and "--local" in sys.argv[1:]:
+ from client.local_mic import Mic
+else:
+ from client.mic import Mic
+
+# Change CWD to $JASPER_HOME/jasper/client
+client_path = os.path.join(os.getenv("JASPER_HOME"), "jasper", "client")
+os.chdir(client_path)
+# Add $JASPER_HOME/jasper/client to sys.path
+sys.path.append(client_path)
+
+# Set $LD_LIBRARY_PATH
+os.environ["LD_LIBRARY_PATH"] = "/usr/local/lib"
+
+# Set $PATH
+path = os.getenv("PATH")
+if path:
+ path = os.pathsep.join([path, "/usr/local/lib/"])
+else:
+ path = "/usr/local/lib/"
+os.environ["PATH"] = path
+
+speaker = speak.newSpeaker()
+
+
+def testConnection():
+ if Diagnostics.check_network_connection():
+ print "CONNECTED TO INTERNET"
+ else:
+ print "COULD NOT CONNECT TO NETWORK"
+ speaker.say(
+ "Warning: I was unable to connect to a network. Parts of the system may not work correctly, depending on your setup.")
+
+
+def fail(message):
+ traceback.print_exc()
+ speaker.say(message)
+ sys.exit(1)
+
+
+def configure():
+ try:
+ print "COMPILING DICTIONARY"
+ vocabcompiler.compile(
+ "sentences.txt", "dictionary.dic", "languagemodel.lm")
+ print "STARTING CLIENT PROGRAM"
+
+ except OSError:
+ print "BOOT FAILURE: OSERROR"
+ fail(
+ "There was a problem starting Jasper. You may be missing the language model and associated files. Please read the documentation to configure your Raspberry Pi.")
+
+ except IOError:
+ print "BOOT FAILURE: IOERROR"
+ fail(
+ "There was a problem starting Jasper. You may have set permissions incorrectly on some part of the filesystem. Please read the documentation to configure your Raspberry Pi.")
+
+ except:
+ print "BOOT FAILURE"
+ fail(
+ "There was a problem starting Jasper. Please read the documentation to configure your Raspberry Pi.")
+
+old_client = os.path.abspath(os.path.join(os.pardir, "old_client"))
+if os.path.exists(old_client):
+ shutil.rmtree(old_client)
+
+if __name__ == "__main__":
+
+ print "==========================================================="
+ print " JASPER The Talking Computer "
+ print " Copyright 2013 Shubhro Saha & Charlie Marsh "
+ print "==========================================================="
+
+ speaker.say("Hello.... I am Jasper... Please wait one moment.")
+ testConnection()
+ configure()
+
+ 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, 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()