Esempio n. 1
0
def _get_responses(domain, recipient, text, yield_responses=False, session_id=None, update_timestamp=True):
    """
    Try to process this message like a session-based submission against
    an xform.
    
    Returns a list of responses if there are any.
    """
    session = None
    if session_id is not None:
        if update_timestamp:
            # The IVR workflow passes the session id
            session = XFormsSession.latest_by_session_id(session_id)
    else:
        # The SMS workflow grabs the open sms session
        session = XFormsSession.get_open_sms_session(domain, recipient)
        if session is not None:
            session_id = session.session_id

    if update_timestamp and session is not None:
        session.modified_time = datetime.utcnow()
        session.save()

    if session_id is not None:
        # TODO auth
        if yield_responses:
            return list(tfsms.next_responses(session_id, text, auth=None))
        else:
            return _responses_to_text(tfsms.next_responses(session_id, text, auth=None))
Esempio n. 2
0
def _get_responses(domain,
                   recipient,
                   text,
                   yield_responses=False,
                   session_id=None,
                   update_timestamp=True):
    """
    Try to process this message like a session-based submission against
    an xform.
    
    Returns a list of responses if there are any.
    """
    session = None
    if session_id is not None:
        if update_timestamp:
            # The IVR workflow passes the session id
            session = SQLXFormsSession.by_session_id(session_id)
    else:
        # The SMS workflow grabs the open sms session
        session = SQLXFormsSession.get_open_sms_session(domain, recipient)
        if session is not None:
            session_id = session.session_id

    if update_timestamp and session is not None:
        session.modified_time = datetime.utcnow()
        session.save()

    if session_id is not None:
        # TODO auth
        if yield_responses:
            return list(tfsms.next_responses(session_id, text, auth=None))
        else:
            return _responses_to_text(
                tfsms.next_responses(session_id, text, auth=None))
Esempio n. 3
0
def _get_responses(domain,
                   recipient,
                   text,
                   yield_responses=False,
                   session_id=None):
    """
    Try to process this message like a session-based submission against
    an xform.
    
    Returns a list of responses if there are any.
    """
    # assumes couch_recipient is the connection_id
    if session_id is not None:
        session = XFormsSession.latest_by_session_id(session_id)
    else:
        # The IVR workflow passes the session id, the SMS workflow grabs the open sms session
        session = XFormsSession.view(
            "smsforms/open_sms_sessions_by_connection",
            key=[domain, recipient],
            include_docs=True).one()
    if session:
        session.modified_time = datetime.utcnow()
        session.save()
        # TODO auth
        if yield_responses:
            return list(
                tfsms.next_responses(session.session_id, text, auth=None))
        else:
            return _responses_to_text(
                tfsms.next_responses(session.session_id, text, auth=None))
Esempio n. 4
0
def get_responses(domain, session_id, text):
    """
    Try to process this message like a session-based submission against
    an xform.
    
    Returns a list of responses if there are any.
    """
    return list(tfsms.next_responses(session_id, text))
Esempio n. 5
0
    def _try_process_as_session_form(self, msg):
        """
        Try to process this message like a session-based submission against
        an xform.
        
        Returns True if the message matches and was processed.
        """
        logger.debug('Attempting to process message as SESSION FORM')
        # check if this connection is in a form session:
        session = self.get_session(msg)
        trigger = self.get_trigger_keyword(msg)
        if not trigger and session is None:
            logger.debug('Not a session form (no session or trigger kw found')

            # catch if they reply to the last text from a previous session; we don't
            # want to send them a confusing error message.
            recent_sess = self.get_recent_session(msg)
            lockout = settings.SMSFORMS_POSTSESSION_LOCKOUT \
                if hasattr(settings, 'SMSFORMS_POSTSESSION_LOCKOUT') \
                else None
            if recent_sess and lockout and datetime.utcnow() < recent_sess.end_time + lockout:
                # if no other handlers handle this message, it will be swallowed (in the default phase)
                self.swallow = True
            return False
        elif trigger and session:
            # mark old session as 'cancelled' and follow process for creating a new one
            logger.debug('Found trigger kw and stale session. Ending old session and starting fresh.')
            session.cancel()
            session = None

        if session:
            logger.debug('Found an existing session, attempting to answer question with message content: %s' % msg.text)
            last_response = api.current_question(session.session_id)
            ans, error_msg = _pre_validate_answer(msg.text, last_response) 
            # we need the last response to figure out what question type this is.
            if error_msg:
                msg.respond("%s for \"%s\"" % (error_msg, session.question_to_prompt(last_response)))
                return True             
            
            responses = tfsms.next_responses(session.session_id, ans, auth=None)
            
        elif trigger:
            logger.debug('Found trigger keyword. Starting a new session')
            session, responses = self._start_session(msg, trigger)
        
        else:
            raise Exception("This is not a legal state. Some of our preconditions failed.")
        
        [msg.respond(session.question_to_prompt(resp)) for resp in responses if resp.text_prompt]
        logger.debug('Completed processing message as part of SESSION FORM')
        return True
Esempio n. 6
0
def _get_responses(domain, recipient, text, yield_responses=False, session_id=None):
    """
    Try to process this message like a session-based submission against
    an xform.
    
    Returns a list of responses if there are any.
    """
        # assumes couch_recipient is the connection_id
    if session_id is not None:
        session = XFormsSession.latest_by_session_id(session_id)
    else:
        # The IVR workflow passes the session id, the SMS workflow grabs the open sms session
        session = XFormsSession.view("smsforms/open_sms_sessions_by_connection", 
                                     key=[domain, recipient],
                                     include_docs=True).one()
    if session:
        session.modified_time = datetime.utcnow()
        session.save()
        # TODO auth
        if yield_responses:
            return list(tfsms.next_responses(session.session_id, text, auth=None))
        else:
            return _responses_to_text(tfsms.next_responses(session.session_id, text, auth=None))
Esempio n. 7
0
def get_responses(msg):
    """
    Try to process this message like a session-based submission against
    an xform.
    
    Returns a list of responses if there are any.
    """
        # assumes couch_recipient is the connection_id
    session = XFormsSession.view("smsforms/open_sessions_by_connection", 
                                 key=[msg.domain, msg.couch_recipient],
                                 include_docs=True).one()
    if session:
        session.modified_time = datetime.utcnow()
        session.save()
        # TODO auth
        return _responses_to_text(tfsms.next_responses(session.session_id, msg.text,
                                                       auth=None))
Esempio n. 8
0
    def _try_process_as_session_form(self, msg):
        """
        Try to process this message like a session-based submission against
        an xform.
        
        Returns True if the message matches and was processed.
        """
        logger.debug('Attempting to process message as SESSION FORM')
        # check if this connection is in a form session:
        session = self.get_session(msg)
        trigger = self.get_trigger_keyword(msg)
        if not trigger and session is None:
            logger.debug('Not a session form (no session or trigger kw found')
            return
        elif trigger and session:
            # mark old session as 'cancelled' and follow process for creating a new one
            logger.debug('Found trigger kw and stale session. Ending old session and starting fresh.')
            session.cancel()
            session = None

        if session:
            logger.debug('Found an existing session, attempting to answer question with message content: %s' % msg.text)
            last_response = api.current_question(session.session_id)
            ans, error_msg = _pre_validate_answer(msg.text, last_response) 
            # we need the last response to figure out what question type this is.
            if error_msg:
                msg.respond("%s for \"%s\"" % (error_msg, _prompt(last_response)))
                return True             
            
            responses = tfsms.next_responses(session.session_id, ans, auth=None)
            
        
        elif trigger:
            logger.debug('Found trigger keyword. Starting a new session')
            session, responses = self._start_session(msg, trigger)
        
        else:
            raise Exception("This is not a legal state. Some of our preconditions failed.")
        
        [msg.respond(_prompt(resp)) for resp in responses if resp.text_prompt]
        logger.debug('Completed processing message as part of SESSION FORM')
        return True
Esempio n. 9
0
    def _try_process_as_whole_form(self, msg):
        """
        Try to process this message like an entire submission against an xform.
        
        Returns True if the message matches and was processed.
        """
        def _match_to_whole_form(msg):
            # for short term, the syntax for whole forms is any message with
            # more than one word in it. First word is taken as keyword and
            # the rest as answers instead of just the keyword (for interactive
            # form entry). This should be smarter, later.
            text = msg.text.strip()
            if text:
                words = text.strip().split()
                if len(words) > 1:
                    return self.get_trigger_keyword(msg)
            return None

        def _break_into_answers(msg):
            # TODO: brittle and not fully featured
            return map(lambda ans: _tf_format(ans)[0],
                       re.split(settings.ANSWER_DELIMITER_RE, msg.text.strip())[1:])

        trigger = _match_to_whole_form(msg)
        if not trigger:
            return
        
        # close any existing sessions
        _close_open_sessions(msg.connection)

        # start the form session
        session, responses = self._start_session(msg, trigger)
        assert len(responses) > 0, "there should be at least 1 response"
        if responses[0].is_error:
            # if the initial session fails, just close the session immediately
            # and return the error
            session.end()
            msg.respond(responses[0].error)
            return True
        
        # loop through answers
        current_question = list(responses)[-1]
        answers = _break_into_answers(msg)
        for i, answer in enumerate(answers):
            logging.debug('Processing answer: %s' % answer)
            
            # Attempt to clean and validate given answer before sending to TF
            answer, validation_error_msg = _pre_validate_answer(answer, current_question)
            if validation_error_msg:
                return _respond_and_end("%s for \"%s\"" % (validation_error_msg, 
                                                           session.question_to_prompt(current_question)),
                                                           msg, session)

            responses = tfsms.next_responses(session.session_id, answer)
            current_question = list(responses)[-1]               
            
            # get the last touchforms response object so that we can validate our answer
            # instead of relying on touchforms and getting back a less than useful error.
            if _handle_xformresponse_error(current_question, msg, session, self.router):
                return True
            
            if i+1 != len(answers) and current_question.event and \
                        current_question.event.type == 'form-complete':
                logger.debug('Form completed but their are extra answers. Silently dropping extras! %s' % (", ".join([str(a) for a in answers[i-1:]])))
                logger.warn('Silently dropping extra answer on Full Form session! Message:%s, connection: %s' % (msg.text, msg.connection))
                # We're done here and the session has been ended (in _next()).
                # TODO: Should we return a response to the user warning them there are extras?
                break

        # play through the remaining questions at the end of the form
        # and if they are all optional, answer them with blanks and 
        # finish.
        session = XFormsSession.objects.get(pk=session.pk)
        if not session.ended: # and trigger.allow_incomplete:
            while not session.ended and responses:
                responses = tfsms.next_responses(session.session_id, "")
                current_question = list(responses)[-1]               
                
                # if any of the remaining items complain about an empty
                # answer, send the response
                if _handle_xformresponse_error(current_question, msg, session, self.router):
                    return True
                
                session = XFormsSession.objects.get(pk=session.pk)
            
        session = XFormsSession.objects.get(pk=session.pk)
        if not session.ended:
            msg.respond("Incomplete form! The first unanswered question is '%s'." %
                        session.question_to_prompt(current_question))
            # for now, manually end the session to avoid
            # confusing the session-based engine
            session.end()

        return True