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
 
5
 
6
 import importlib
6
 import importlib
7
 import sys
7
 import sys
8
+import subprocess
8
 import signal
9
 import signal
9
 import os.path
10
 import os.path
10
 from gi.repository import GObject, GLib
11
 from gi.repository import GObject, GLib
25
         # Load configuration
26
         # Load configuration
26
         self.config = Config()
27
         self.config = Config()
27
         self.options = vars(self.config.options)
28
         self.options = vars(self.config.options)
28
-        self.commands = self.options['plugins']['.shell']
29
 
29
 
30
         # Load plugins
30
         # Load plugins
31
         self.plugins = []
31
         self.plugins = []
32
         for plugin in self.options['plugins'].keys():
32
         for plugin in self.options['plugins'].keys():
33
             pmod = importlib.import_module(plugin, 'kayleevc.plugins')
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
         # Create a hasher
36
         # Create a hasher
37
         self.hasher = Hasher(self.config)
37
         self.hasher = Hasher(self.config)
72
         # Connect the recognizer's finished signal to all the plugins
72
         # Connect the recognizer's finished signal to all the plugins
73
         for plugin in self.plugins:
73
         for plugin in self.plugins:
74
             self.recognizer.connect('finished', plugin.recognizer_finished)
74
             self.recognizer.connect('finished', plugin.recognizer_finished)
75
+            plugin.connect('processed', self.plugin_processed)
75
 
76
 
76
     def update_voice_commands_if_changed(self):
77
     def update_voice_commands_if_changed(self):
77
         """Use hashes to test if the voice commands have changed"""
78
         """Use hashes to test if the voice commands have changed"""
105
                     strings.write(word + " ")
106
                     strings.write(word + " ")
106
             strings.write("\n")
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
         if self.options['history']:
120
         if self.options['history']:
110
-            self.history.append(text)
121
+            self.history.append("{}: {}".format(plugin.name, text))
111
             if len(self.history) > self.options['history']:
122
             if len(self.history) > self.options['history']:
112
                 # Pop off the first item
123
                 # Pop off the first item
113
                 self.history.pop(0)
124
                 self.history.pop(0)
117
                 for line in self.history:
128
                 for line in self.history:
118
                     hfile.write(line + '\n')
129
                     hfile.write(line + '\n')
119
 
130
 
120
-    def recognizer_finished(self, recognizer, text):
121
-        pass
122
-
123
     def run(self):
131
     def run(self):
124
         if self.ui:
132
         if self.ui:
125
             self.ui.run()
133
             self.ui.run()

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

5
 
5
 
6
 from abc import ABCMeta, abstractmethod
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
     """Base class for Kaylee plugins
17
     """Base class for Kaylee plugins
11
 
18
 
12
     Each Kaylee plugin module must define a subclass of this class, named
19
     Each Kaylee plugin module must define a subclass of this class, named
13
     ``Plugin``.
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
         """Initialize the plugin
29
         """Initialize the plugin
18
 
30
 
19
         All strings to be included in the corpus should be elements of
31
         All strings to be included in the corpus should be elements of
21
         that the words required for number support are automatically included,
33
         that the words required for number support are automatically included,
22
         so they need not be listed explicitly on a per-plugin basis.
34
         so they need not be listed explicitly on a per-plugin basis.
23
         """
35
         """
36
+        super().__init__()
24
         self.config = config
37
         self.config = config
38
+        self.name = name
25
         self.corpus_strings = set()
39
         self.corpus_strings = set()
26
 
40
 
27
     @abstractmethod
41
     @abstractmethod
29
         """Process a recognized voice command
43
         """Process a recognized voice command
30
 
44
 
31
         This method must return True if the command was handled by the plugin,
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
         pass
50
         pass

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

11
 
11
 
12
 class Plugin(PluginBase):
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
         self.options = vars(self.config.options)
16
         self.options = vars(self.config.options)
17
         self.number_parser = NumberParser()
17
         self.number_parser = NumberParser()
18
-        self.commands = self.options['plugins']['.shell']
18
+        self.commands = self.options['plugins'][name]
19
 
19
 
20
         for voice_cmd in self.commands.keys():
20
         for voice_cmd in self.commands.keys():
21
             self.corpus_strings.add(voice_cmd.strip().replace('%d', ''))
21
             self.corpus_strings.add(voice_cmd.strip().replace('%d', ''))
34
         numt, nums = self.number_parser.parse_all_numbers(text)
34
         numt, nums = self.number_parser.parse_all_numbers(text)
35
         # Is there a matching command?
35
         # Is there a matching command?
36
         if text in self.commands:
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
             cmd = self.commands[text]
38
             cmd = self.commands[text]
43
             self._run_command(cmd)
39
             self._run_command(cmd)
44
-            # TODO: Logging can be handled along with valid_sentence_command
45
-            #self.log_history(text)
46
             return True
40
             return True
47
         elif numt in self.commands:
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
             cmd = self.commands[numt]
43
             cmd = self.commands[numt]
53
             cmd = cmd.format(*nums)
44
             cmd = cmd.format(*nums)
54
             self._run_command(cmd)
45
             self._run_command(cmd)
55
-            #self.log_history(text)
56
             return True
46
             return True
57
         else:
47
         else:
58
             # TODO: This could be implemented as a plugin, implicitly loaded
48
             # TODO: This could be implemented as a plugin, implicitly loaded

Loading…
Cancel
Save