class CollectDigitsRunner(Runner): """User's single interaction to enter a set of digits Note: Asterisk is hard-coded to use # to exit the entry-mode... """ def __call__(self, *args, **named): """Begin the AGI processing for the menu""" self.readDigits() return self.finalDF def readDigits(self, result=None): """Begin process of reading digits from the user""" soundFile = getattr(self.model, 'soundFile', None) if soundFile: # easiest possibility, just read out the file... return self.agi.getData( soundFile, timeout=self.model.timeout, maxDigits = getattr(self.model, 'maxDigits', None), ).addCallback(self.onReadDigits).addErrback(self.returnError) else: raise NotImplemented("""Haven't got non-soundfile menus working yet""") self.agi.getData(self.menu. filename, timeout=2.000, maxDigits=None) def validEntry(self, digits): """Determine whether given digits are considered a "valid" entry""" minDigits = getattr(self.model, 'minDigits', None) if minDigits is not None: if len(digits) < minDigits: return False, 'Too few digits' return True, None def onReadDigits(self, (digits,timeout)): """Deal with succesful result from reading digits""" log.info("""onReadDigits: %r, %s""", digits, timeout) valid, reason = self.validEntry(digits) if (not digits) and (not timeout): # user pressed # raise error.MenuExit( self.model, """User cancelled entry of digits""", ) if not valid: if self.model.tellInvalid: # this should be a menu, letting the user decide to re-enter, # or cancel entry pass self.alreadyRepeated += 1 if self.alreadyRepeated >= self.model.maxRepetitions: log.warn("""User did not complete digit-entry for %s, timing out""", self.model) raise error.MenuTimeout( self.model, """User did not finish digit-entry in %s passes of collection""" % ( self.alreadyRepeated, ) ) return self.readDigits() else: # Yay, we got a valid response! return self.returnResult([(self, digits)])
def onReadDigits(self, values): """Deal with succesful result from reading digits""" (digits, timeout) = values log.info("""onReadDigits: %r, %s""", digits, timeout) valid, reason = self.validEntry(digits) if (not digits) and (not timeout): # user pressed # raise error.MenuExit( self.model, """User cancelled entry of digits""", ) if not valid: if self.model.tellInvalid: # this should be a menu, letting the user decide to re-enter, # or cancel entry pass self.alreadyRepeated += 1 if self.alreadyRepeated >= self.model.maxRepetitions: log.warn( """User did not complete digit-entry for %s, timing out""", self.model) raise error.MenuTimeout( self.model, """User did not finish digit-entry in %s passes of collection""" % (self.alreadyRepeated, )) return self.readDigits() else: # Yay, we got a valid response! return self.returnResult([(self, digits)])
def onReadMenu(self, pressed): """Deal with succesful result from reading menu""" log.info("""onReadMenu: %r""", pressed) if not pressed: self.alreadyRepeated += 1 if self.alreadyRepeated >= self.model.maxRepetitions: log.warn( """User did not complete menu selection for %s, timing out""", self.model) if not self.finalDF.called: raise error.MenuTimeout( self.model, """User did not finish selection in %s passes of menu""" % (self.alreadyRepeated, )) return None return self.readMenu() else: # Yay, we got an escape-key pressed for option in self.model.options: if pressed in option.option: if callable(option): # allow for chaining down into sub-menus and the like... # we return the result of calling the option via self.finalDF return defer.maybeDeferred(option, pressed, self).addCallbacks( self.returnResult, self.returnError) elif hasattr(option, 'onSuccess'): return defer.maybeDeferred(option.onSuccess, pressed, self).addCallbacks( self.returnResult, self.returnError) else: return self.returnResult([ (option, pressed), ]) # but it wasn't anything we expected... if not self.model.tellInvalid: raise error.MenuUnexpectedOption( self.model, """User somehow selected %r, which isn't a recognised option?""" % (pressed, ), ) else: return self.agi.getOption( self.model.INVALID_OPTION_FILE, self.escapeDigits, timeout=0, ).addCallback(self.readMenu).addErrback(self.returnError)