[olpc-nz] Speak answering maths questions

Tim McNamara paperless at timmcnamara.co.nz
Sat May 29 02:08:27 EDT 2010


Hi all,

*About*
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.

This is mainly addressed at Sugar devs, but thought Alastair & Grant from
Wellington this morning might be interested.


*Background  => User story*
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?


*First thoughts*
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:

if text.rstrip()[-1:] == '=':
    try:
        text = str(eval(text.rstrip()[:-1]))
    except:
    # SyntaxError is called when eval can't handle the input,
    # but I think we should obscure all errors
        pass

We need to convert the result of the eval to a string because speaking seems
to get confused when it encounters an integer. The practical result of the
code above is to produce results similar to this below:

>>> text = '3+4= '
>>> str(eval(text.rstrip()[:-1]))
'7'

One problem with this approach is integer division. Speak I am not quite
sure yet how to simply convert all integers within text to floats and keep
things succint & snappy. I thought it would be better to bring something
back to the list and have a chat about trade offs.

Also, what do people think about the risks of exposing eval() to users? My
thoughts are that as we're forcing an '=' to be at the end of the string, it
will always result in SyntaxErrors. if something dangerous is attempted to
be evaluated.

>>> eval('str')
<type 'str'>

vs.

>>> eval('str=')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    str=
      ^
SyntaxError: unexpected EOF while parsing


Even so, is there a simple way to expose mathematical operators but prevent
calls to functions & methods?

*Where to implement?*

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.

it is very easy to tel someone 'add an equals sign at the end'. However,
since we have a Robot mode, it's probably best to throw it in there [2].
However, it's probably best for code maintenance to put the evaluation in
the 'brain.py', rather than in the voice, so that's where solution [3] puts
it.

[1] activity.py trunk:377

Will evaluate maths problems agnostic of mode:

def _entry_activate_cb(self, entry):
    # the user pressed Return, say the text and clear it out
    text = entry.props.text.rstrip()
    maths_problem = False
    if text:
        self.face.look_ahead()

        # solve the maths problem
        if text[-1:] == '=':
            try:
                text = str(eval(text[:-1]))
                maths_problem = True
            except:
                pass

        # speak the text
        if self._mode == MODE_BOT and not maths_problem:
            self.face.say(
                    brain.respond(self.voices.props.value, text))
        else:
            self.face.say(text)

[2] activity.py trunk:377

Will evaluate maths only if we're in robot mode

def _entry_activate_cb(self, entry):
    # the user pressed Return, say the text and clear it out
    text = entry.props.text.rstrip()
    if text:
        self.face.look_ahead()

        # speak the text
        if self._mode == MODE_BOT:
            if text[-1:] == '=':
                try:
                    text = str(eval(text[:-1]))
                except:
                    self.face.say(text)
            self.face.say(
                    brain.respond(self.voices.props.value, text))
        else:
            self.face.say(text)


[3] brain.py trunk:66:

Moving the logic from [2], to the "brain" of the program.

def respond(voice, text):
    if text.rstrip()[-1:] == '=':
        try:
            text = str(eval(text.rstrip()[:-1]))
        except:
            pass

    if _kernel is None:
        return text
    else:
        return _kernel.respond(text)

*Summary*

I personally prefer [1] because it means that you don't need to change
modes. However, that does mean we're overloading the default speech mode. If
there's no support, then [3] is probably be the best way to keep logic in
the right place.

Thanks
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.laptop.org/pipermail/olpc-nz/attachments/20100529/ad3c3833/attachment.htm 


More information about the olpc-nz mailing list