Hi all,<br><br><b>About</b><br>This email is asking for feedback on different alternatives of implementing a feedback request. It also goes through a lot of thought processes that have resulted in my preferred suggestion.<br>
<br>This is mainly addressed at Sugar devs, but thought Alastair &amp; Grant
 from Wellington this morning might be interested.<br><br><br><b>Background  =&gt; User story</b><br>This morning while we were testing with some parents, the question came up whether Speak could respond to maths problems, e.g. if you entered 10+2, would speak respond with 12?<br>
<br><br><b>First thoughts</b><br>I thought this was quite a neat suggestion and have encountered a few ways to implement it. To start with, I thought a smart way to indicate that you wanted Speak to tell you an answer would be to use the equals sign after maths. That leads to a construction like this:<br>
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">if text.rstrip()[-1:] == &#39;=&#39;:</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    try:</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        text = str(eval(text.rstrip()[:-1]))</span><span style="font-family: courier new,monospace;"></span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    except:<br>
</span><span style="font-family: courier new,monospace;">    # SyntaxError is 
called when eval can&#39;t handle the input, </span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    # but I think we 
should obscure all errors</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        pass<br><font face="arial,helvetica,sans-serif"><br></font></span>We need to convert the result of the eval to a string because speaking 
seems to get confused when it encounters an integer. <span style="font-family: courier new,monospace;"><font face="arial,helvetica,sans-serif">The practical result of the code above is to produce results similar to this below:   </font><br>
<br>&gt;&gt;&gt; text = &#39;3+4= &#39;<br>&gt;&gt;&gt; str(eval(text.rstrip()[:-1]))<br>&#39;7&#39;<br></span><br>One problem with this approach is integer division. Speak I am not quite sure yet how to simply convert all integers within <span style="font-family: courier new,monospace;">text</span> to floats and keep things succint &amp; snappy. I thought it would be better to bring something back to the list and have a chat about trade offs.<br>
<br>Also, what do people think about the risks of exposing <span style="font-family: courier new,monospace;">eval()</span> to users? My thoughts are that as we&#39;re forcing an &#39;=&#39; to be at the end of the string, it will always result in SyntaxErrors. if something dangerous is attempted to be evaluated.<br>
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&gt;&gt;&gt; eval(&#39;str&#39;)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&lt;type &#39;str&#39;&gt;</span><br>
<br>vs.<br><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&gt;&gt;&gt; eval(&#39;str=&#39;)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">Traceback (most recent call last):</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">  File &quot;&lt;string&gt;&quot;, line 1</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    str=</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">      ^</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">SyntaxError: unexpected EOF while parsing</span><br><br><br>Even so, is there a simple way to expose mathematical operators but prevent calls to functions &amp; methods?<br>
<span style="font-family: courier new,monospace;"><font face="arial,helvetica,sans-serif"><br></font></span><b>Where to implement?</b><br><br>The easiest place for the user would be to be agnostic of mode [1]. E.g. whenever an equals sign appears at the end, try to answer the maths and say the result. <br>
<br>it is very easy to tel someone &#39;add an equals sign at the end&#39;. However, since we have a Robot mode, it&#39;s probably best to throw it in there [2]. However, it&#39;s probably best for code maintenance to put the evaluation in the &#39;brain.py&#39;, rather than in the voice, so that&#39;s where solution [3] puts it.<br>
<br>[1] activity.py trunk:377<br><br>Will evaluate maths problems agnostic of mode:<br><br><span style="font-family: courier new,monospace;">def _entry_activate_cb(self, entry):<br>    # the user pressed Return, say the text and clear it out<br>
    text = entry.props.text.rstrip()<br>    maths_problem = False<br>    if text:<br>        self.face.look_ahead()<br><br>        # solve the maths problem<br>        if text[-1:] == &#39;=&#39;:<br>            try:<br>                text = str(eval(text[:-1]))<br>
                maths_problem = True<br>            except:<br>                pass<br><br>        # speak the text<br>        if self._mode == MODE_BOT and not maths_problem:<br>            self.face.say(<br>                    brain.respond(self.voices.props.value, text))<br>
        else:<br>            self.face.say(text)</span><br><br>[2] activity.py trunk:377<br><br>Will evaluate maths only if we&#39;re in robot mode<br><br><span style="font-family: courier new,monospace;">def _entry_activate_cb(self, entry):<br>
    # the user pressed Return, say the text and clear it out<br>    text = entry.props.text.rstrip()<br>    if text:<br>        self.face.look_ahead()<br><br>        # speak the text<br>        if self._mode == MODE_BOT:<br>
            if text[-1:] == &#39;=&#39;:<br>                try:<br>                    text = str(eval(text[:-1]))<br>                except:<br>                    self.face.say(text)<br>            self.face.say(<br>                    brain.respond(self.voices.props.value, text))<br>
        else:<br>            self.face.say(text)</span><br><br><br>[3] brain.py trunk:66:<br><br>Moving the logic from [2], to the &quot;brain&quot; of the program.<br><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">def respond(voice, text):</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    if text.rstrip()[-1:] == &#39;=&#39;:</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        try:</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            text = str(eval(text.rstrip()[:-1]))</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        except:</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            pass</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    if _kernel is None:</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        return text</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    else:</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        return _kernel.respond(text)</span><br><br><b>Summary</b><br><br>I personally prefer [1] because it means that you don&#39;t need to change modes. However, that does mean we&#39;re overloading the default speech mode. If there&#39;s no support, then [3] is probably be the best way to keep logic in the right place. <br>
<br>Thanks <br>