Browse Source

Delegate history and valid command to Kaylee

The plugins shouldn't be in charge of running the
valid_sentence_command.  They also certainly shouldn't have to manage
the history file.  Both of these functions have been delegated to the
Kaylee object, by way of a 'processed' signal emitted by a plugin as
soon as it knows it will handle the voice command.
Clara Hobbs 7 years ago
parent
commit
56c6954039
3 changed files with 39 additions and 25 deletions
  1. 15
    7
      kayleevc/kaylee.py
  2. 19
    3
      kayleevc/plugins/pluginbase.py
  3. 5
    15
      kayleevc/plugins/shell.py

+ 15
- 7
kayleevc/kaylee.py View File

@@ -5,6 +5,7 @@
5 5
 
6 6
 import importlib
7 7
 import sys
8
+import subprocess
8 9
 import signal
9 10
 import os.path
10 11
 from gi.repository import GObject, GLib
@@ -25,13 +26,12 @@ class Kaylee:
25 26
         # Load configuration
26 27
         self.config = Config()
27 28
         self.options = vars(self.config.options)
28
-        self.commands = self.options['plugins']['.shell']
29 29
 
30 30
         # Load plugins
31 31
         self.plugins = []
32 32
         for plugin in self.options['plugins'].keys():
33 33
             pmod = importlib.import_module(plugin, 'kayleevc.plugins')
34
-            self.plugins.append(pmod.Plugin(self.config))
34
+            self.plugins.append(pmod.Plugin(self.config, plugin))
35 35
 
36 36
         # Create a hasher
37 37
         self.hasher = Hasher(self.config)
@@ -72,6 +72,7 @@ class Kaylee:
72 72
         # Connect the recognizer's finished signal to all the plugins
73 73
         for plugin in self.plugins:
74 74
             self.recognizer.connect('finished', plugin.recognizer_finished)
75
+            plugin.connect('processed', self.plugin_processed)
75 76
 
76 77
     def update_voice_commands_if_changed(self):
77 78
         """Use hashes to test if the voice commands have changed"""
@@ -105,9 +106,19 @@ class Kaylee:
105 106
                     strings.write(word + " ")
106 107
             strings.write("\n")
107 108
 
108
-    def log_history(self, text):
109
+    def plugin_processed(self, plugin, text):
110
+        """Callback for ``processed`` signal from plugins
111
+
112
+        Runs the valid_sentence_command and logs the recognized sentence to the
113
+        history file.
114
+        """
115
+        # Run the valid_sentence_command if it's set
116
+        if self.options['valid_sentence_command']:
117
+            subprocess.call(self.options['valid_sentence_command'], shell=True)
118
+
119
+        # Log the command to the history file
109 120
         if self.options['history']:
110
-            self.history.append(text)
121
+            self.history.append("{}: {}".format(plugin.name, text))
111 122
             if len(self.history) > self.options['history']:
112 123
                 # Pop off the first item
113 124
                 self.history.pop(0)
@@ -117,9 +128,6 @@ class Kaylee:
117 128
                 for line in self.history:
118 129
                     hfile.write(line + '\n')
119 130
 
120
-    def recognizer_finished(self, recognizer, text):
121
-        pass
122
-
123 131
     def run(self):
124 132
         if self.ui:
125 133
             self.ui.run()

+ 19
- 3
kayleevc/plugins/pluginbase.py View File

@@ -5,15 +5,27 @@
5 5
 
6 6
 from abc import ABCMeta, abstractmethod
7 7
 
8
+from gi.repository import GObject
9
+from gi.types import GObjectMeta
8 10
 
9
-class PluginBase(metaclass=ABCMeta):
11
+
12
+class GObjectABCMeta(ABCMeta, GObjectMeta):
13
+    pass
14
+
15
+
16
+class PluginBase(GObject.Object, metaclass=GObjectABCMeta):
10 17
     """Base class for Kaylee plugins
11 18
 
12 19
     Each Kaylee plugin module must define a subclass of this class, named
13 20
     ``Plugin``.
14 21
     """
15 22
 
16
-    def __init__(self, config):
23
+    __gsignals__ = {
24
+        'processed' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
25
+                      (GObject.TYPE_STRING,))
26
+    }
27
+
28
+    def __init__(self, config, name):
17 29
         """Initialize the plugin
18 30
 
19 31
         All strings to be included in the corpus should be elements of
@@ -21,7 +33,9 @@ class PluginBase(metaclass=ABCMeta):
21 33
         that the words required for number support are automatically included,
22 34
         so they need not be listed explicitly on a per-plugin basis.
23 35
         """
36
+        super().__init__()
24 37
         self.config = config
38
+        self.name = name
25 39
         self.corpus_strings = set()
26 40
 
27 41
     @abstractmethod
@@ -29,6 +43,8 @@ class PluginBase(metaclass=ABCMeta):
29 43
         """Process a recognized voice command
30 44
 
31 45
         This method must return True if the command was handled by the plugin,
32
-        and False otherwise.
46
+        and False otherwise.  As soon as it has been determined that the
47
+        command will be handled, this method must emit a ``processed`` signal
48
+        with ``text`` as its parameter.
33 49
         """
34 50
         pass

+ 5
- 15
kayleevc/plugins/shell.py View File

@@ -11,11 +11,11 @@ from ..numbers import NumberParser
11 11
 
12 12
 class Plugin(PluginBase):
13 13
 
14
-    def __init__(self, config):
15
-        super().__init__(config)
14
+    def __init__(self, config, name):
15
+        super().__init__(config, name)
16 16
         self.options = vars(self.config.options)
17 17
         self.number_parser = NumberParser()
18
-        self.commands = self.options['plugins']['.shell']
18
+        self.commands = self.options['plugins'][name]
19 19
 
20 20
         for voice_cmd in self.commands.keys():
21 21
             self.corpus_strings.add(voice_cmd.strip().replace('%d', ''))
@@ -34,25 +34,15 @@ class Plugin(PluginBase):
34 34
         numt, nums = self.number_parser.parse_all_numbers(text)
35 35
         # Is there a matching command?
36 36
         if text in self.commands:
37
-            # Run the valid_sentence_command if it's set
38
-            # TODO: determine how to support this with plugins
39
-            if self.options['valid_sentence_command']:
40
-                subprocess.call(self.options['valid_sentence_command'],
41
-                                shell=True)
37
+            self.emit('processed', text)
42 38
             cmd = self.commands[text]
43 39
             self._run_command(cmd)
44
-            # TODO: Logging can be handled along with valid_sentence_command
45
-            #self.log_history(text)
46 40
             return True
47 41
         elif numt in self.commands:
48
-            # Run the valid_sentence_command if it's set
49
-            if self.options['valid_sentence_command']:
50
-                subprocess.call(self.options['valid_sentence_command'],
51
-                                shell=True)
42
+            self.emit('processed', text)
52 43
             cmd = self.commands[numt]
53 44
             cmd = cmd.format(*nums)
54 45
             self._run_command(cmd)
55
-            #self.log_history(text)
56 46
             return True
57 47
         else:
58 48
             # TODO: This could be implemented as a plugin, implicitly loaded

Loading…
Cancel
Save