Example #1
0
 def _get_case(session):
     session = XFormsSession.get(session.get_id)
     self.assertTrue(session.submission_id)
     instance = XFormInstance.get(session.submission_id)
     case_id = instance.xpath("form/case/@case_id")
     self.assertTrue(case_id)
     return CommCareCase.get(case_id)
Example #2
0
 def _get_case(session):
     session = XFormsSession.get(session.get_id)
     self.assertTrue(session.submission_id)
     instance = XFormInstance.get(session.submission_id)
     case_id = instance.xpath("form/case/@case_id")
     self.assertTrue(case_id)
     return CommCareCase.get(case_id)
Example #3
0
    def test_basic_form_playing(self):
        # load the app
        with open(
                os.path.join(os.path.dirname(__file__), "data",
                             "demo_app.json")) as f:
            app_json = json.loads(f.read())
            app = import_app(app_json, self.domain)

        # start form session
        session, responses = start_session(self.domain, self.contact, app,
                                           app.get_module(0),
                                           app.get_module(0).get_form(0))

        [answer] = responses
        self.assertEqual("what is your name?", answer)

        # check state of model
        self.assertEqual(session.start_time, session.modified_time)
        self.assertEqual("http://www.commcarehq.org/tests/smsforms",
                         session.form_xmlns)
        self.assertFalse(session.end_time)
        self.assertEqual(False, session.completed)
        self.assertEqual(self.domain, session.domain)
        self.assertEqual(self.contact.get_id, session.user_id)
        self.assertEqual(app.get_id, session.app_id)
        self.assertFalse(session.submission_id)

        # play through the form, checking answers
        q_and_a(self, "sms contact", "how old are you, sms contact?",
                self.domain)
        q_and_a(self, "29", "what is your gender? 1:male, 2:female",
                self.domain)
        q_and_a(self, "2", "thanks for submitting!", self.domain)

        # check the instance
        session = XFormsSession.get(session.get_id)
        self.assertTrue(session.submission_id)
        instance = XFormInstance.get(session.submission_id)
        self.assertEqual("sms contact", instance.xpath("form/name"))
        self.assertEqual("29", instance.xpath("form/age"))
        self.assertEqual("f", instance.xpath("form/gender"))
        self.assertEqual(self.domain, instance.domain)
Example #4
0
    def test_reverse_sync(self):
        properties = _arbitrary_session_properties()
        couch_session = XFormsSession(**properties)
        couch_session.save()
        sql_session = SQLXFormsSession.objects.get(couch_id=couch_session._id)
        for prop, value in properties.items():
            self.assertEqual(getattr(sql_session, prop), value)

        # make sure we didn't do any excess saves
        self.assertTrue(XFormsSession.get_db().get_rev(couch_session._id).startswith('1-'))

        updated_properties = _arbitrary_session_properties()
        for prop, value in updated_properties.items():
            setattr(sql_session, prop, value)
        sql_session.save()

        couch_session = XFormsSession.get(couch_session._id)
        for prop, value in updated_properties.items():
            self.assertEqual(getattr(couch_session, prop), value)
        self.assertTrue(couch_session._rev.startswith('2-'))
Example #5
0
 def test_basic_form_playing(self):
     # load the app
     with open(os.path.join(os.path.dirname(__file__), "data", "demo_app.json")) as f:
         app_json = json.loads(f.read())
         app = import_app(app_json, self.domain)
         
     # start form session
     session, responses = start_session(self.domain, self.contact, app, 
                                        app.get_module(0), 
                                        app.get_module(0).get_form(0))
     
     [answer] = responses 
     self.assertEqual("what is your name?", answer)
     
     # check state of model
     self.assertEqual(session.start_time, session.modified_time)
     self.assertEqual("http://www.commcarehq.org/tests/smsforms", session.form_xmlns)
     self.assertFalse(session.end_time)
     self.assertEqual(False, session.completed)
     self.assertEqual(self.domain, session.domain)
     self.assertEqual(self.contact.get_id, session.user_id)
     self.assertEqual(app.get_id, session.app_id)
     self.assertFalse(session.submission_id)
     
     # play through the form, checking answers
     q_and_a(self, "sms contact", "how old are you, sms contact?", self.domain)
     q_and_a(self, "29", "what is your gender? 1:male, 2:female", self.domain)
     q_and_a(self, "2", "thanks for submitting!", self.domain)
     
     # check the instance
     session = XFormsSession.get(session.get_id)
     self.assertTrue(session.submission_id)
     instance = XFormInstance.get(session.submission_id)
     self.assertEqual("sms contact", instance.xpath("form/name"))
     self.assertEqual("29", instance.xpath("form/age"))
     self.assertEqual("f", instance.xpath("form/gender"))
     self.assertEqual(self.domain, instance.domain)
Example #6
0
def structured_sms_handler(verified_number, text):

    # Circular Import
    from corehq.apps.reminders.models import SurveyKeyword, FORM_TYPE_ALL_AT_ONCE

    text = text.strip()
    if text == "":
        return False
    for survey_keyword in SurveyKeyword.get_all(verified_number.domain):
        if survey_keyword.form_type == FORM_TYPE_ALL_AT_ONCE:
            if survey_keyword.delimiter is not None:
                args = text.split(survey_keyword.delimiter)
            else:
                args = text.split()

            keyword = args[0].strip().upper()
            if keyword != survey_keyword.keyword.upper():
                continue

            try:
                error_occurred = False
                error_msg = ""
                form_complete = False

                # Close any open sessions
                close_open_sessions(verified_number.domain, verified_number.owner_id)

                # Start the session
                form = Form.get_form(survey_keyword.form_unique_id)
                app = form.get_app()
                module = form.get_module()

                if verified_number.owner_doc_type == "CommCareCase":
                    case_id = verified_number.owner_id
                else:
                    # TODO: Need a way to choose the case when it's a user that's playing the form
                    case_id = None

                session, responses = start_session(
                    verified_number.domain,
                    verified_number.owner,
                    app,
                    module,
                    form,
                    case_id=case_id,
                    yield_responses=True,
                )
                assert len(responses) > 0, "There should be at least one response."

                current_question = responses[-1]
                form_complete = is_form_complete(current_question)

                if not form_complete:
                    if survey_keyword.use_named_args:
                        # Arguments in the sms are named
                        xpath_answer = {}  # Dictionary of {xpath : answer}
                        for answer in args[1:]:
                            answer = answer.strip()
                            answer_upper = answer.upper()
                            if survey_keyword.named_args_separator is not None:
                                # A separator is used for naming arguments; for example, the "=" in "register name=joe age=25"
                                answer_parts = answer.partition(survey_keyword.named_args_separator)
                                if answer_parts[1] != survey_keyword.named_args_separator:
                                    error_occurred = True
                                    error_msg = "ERROR: Expected name and value to be joined by" + (
                                        " '%s'" % survey_keyword.named_args_separator
                                    )
                                    break
                                else:
                                    arg_name = answer_parts[0].upper().strip()
                                    xpath = survey_keyword.named_args.get(arg_name, None)
                                    if xpath is not None:
                                        if xpath in xpath_answer:
                                            error_occurred = True
                                            error_msg = "ERROR: More than one answer found for" + (" '%s'" % arg_name)
                                            break

                                        xpath_answer[xpath] = answer_parts[2].strip()
                                    else:
                                        # Ignore unexpected named arguments
                                        pass
                            else:
                                # No separator is used for naming arguments; for example, "update a100 b34 c5"
                                matches = 0
                                for k, v in survey_keyword.named_args.items():
                                    if answer_upper.startswith(k):
                                        matches += 1
                                        if matches > 1:
                                            error_occurred = True
                                            error_msg = "ERROR: More than one question matches" + (" '%s'" % answer)
                                            break

                                        if v in xpath_answer:
                                            error_occurred = True
                                            error_msg = "ERROR: More than one answer found for" + (" '%s'" % k)
                                            break

                                        xpath_answer[v] = answer[len(k) :].strip()

                                if matches == 0:
                                    # Ignore unexpected named arguments
                                    pass

                            if error_occurred:
                                break

                        # Go through each question in the form, answering only the questions that the sms has answers for
                        while not form_complete and not error_occurred:
                            if current_question.is_error:
                                error_occurred = True
                                error_msg = current_question.text_prompt or "ERROR: Internal server error"
                                break

                            xpath = current_question.event._dict["binding"]
                            if xpath in xpath_answer:
                                valid, answer, _error_msg = validate_answer(current_question.event, xpath_answer[xpath])
                                if not valid:
                                    error_occurred = True
                                    error_msg = "ERROR: " + _error_msg
                                    break
                                responses = _get_responses(
                                    verified_number.domain, verified_number.owner_id, answer, yield_responses=True
                                )
                            else:
                                responses = _get_responses(
                                    verified_number.domain, verified_number.owner_id, "", yield_responses=True
                                )

                            current_question = responses[-1]
                            if is_form_complete(current_question):
                                form_complete = True
                    else:
                        # Arguments in the sms are not named; pass each argument to each question in order
                        for answer in args[1:]:
                            if form_complete:
                                # Form is complete, ignore remaining answers
                                break

                            if current_question.is_error:
                                error_occurred = True
                                error_msg = current_question.text_prompt or "ERROR: Internal server error"
                                break

                            valid, answer, _error_msg = validate_answer(current_question.event, answer.strip())
                            if not valid:
                                error_occurred = True
                                error_msg = "ERROR: " + _error_msg
                                break

                            responses = _get_responses(
                                verified_number.domain, verified_number.owner_id, answer, yield_responses=True
                            )
                            current_question = responses[-1]
                            form_complete = is_form_complete(current_question)

                        # If the form isn't finished yet but we're out of arguments, try to leave each remaining question blank and continue
                        while not form_complete and not error_occurred:
                            responses = _get_responses(
                                verified_number.domain, verified_number.owner_id, "", yield_responses=True
                            )
                            current_question = responses[-1]

                            if current_question.is_error:
                                error_occurred = True
                                error_msg = current_question.text_prompt or "ERROR: Internal server error"

                            if is_form_complete(current_question):
                                form_complete = True
            except Exception:
                logging.exception(
                    "Could not process structured sms for verified number %s, domain %s, keyword %s"
                    % (verified_number._id, verified_number.domain, keyword)
                )
                error_occurred = True
                error_msg = "ERROR: Internal server error"

            if error_occurred:
                send_sms_to_verified_number(verified_number, error_msg)

            if error_occurred or not form_complete:
                session = XFormsSession.get(session._id)
                session.end(False)
                session.save()

            return True

    return False
Example #7
0
def handle_structured_sms(survey_keyword, survey_keyword_action, contact,
    verified_number, text, send_response=False, msg=None, case=None,
    text_args=None):

    domain = contact.domain
    contact_doc_type = contact.doc_type
    contact_id = contact._id

    if text_args is not None:
        args = text_args
    else:
        args = split_args(text, survey_keyword)
        args = args[1:]
    keyword = survey_keyword.keyword.upper()

    error_occurred = False
    error_msg = None
    session = None

    try:
        # Start the session
        app, module, form = get_form(
            survey_keyword_action.form_unique_id, include_app_module=True)

        if case:
            case_id = case._id
        elif contact_doc_type == "CommCareCase":
            case_id = contact_id
        else:
            case_id = None

        session, responses = start_session(domain, contact, app, module,
            form, case_id=case_id, yield_responses=True)
        session.workflow = WORKFLOW_KEYWORD
        session.save()
        assert len(responses) > 0, "There should be at least one response."

        first_question = responses[-1]
        if not is_form_complete(first_question):
            if survey_keyword_action.use_named_args:
                # Arguments in the sms are named
                xpath_answer = parse_structured_sms_named_args(args,
                    survey_keyword_action, verified_number)
                _handle_structured_sms(domain, args, contact_id, session,
                    first_question, verified_number, xpath_answer)
            else:
                # Arguments in the sms are not named; pass each argument to
                # each question in order
                _handle_structured_sms(domain, args, contact_id, session,
                    first_question, verified_number)

    except StructuredSMSException as sse:
        error_occurred = True
        error_msg = ""
        if sse.xformsresponse and sse.xformsresponse.event:
            xpath_arg = None
            if survey_keyword_action.use_named_args:
                xpath_arg = \
                    {v: k for k, v in survey_keyword_action.named_args.items()}
            field_name = get_question_id(sse.xformsresponse, xpath_arg)
            error_msg = get_message(MSG_FIELD_DESCRIPTOR, verified_number,
                (field_name,))
        error_msg = "%s%s" % (error_msg, sse.response_text)
    except Exception:
        logging.exception("Could not process structured sms for contact %s, "
            "domain %s, keyword %s" % (contact_id, domain, keyword))
        error_occurred = True
        error_msg = get_message(MSG_TOUCHFORMS_ERROR, verified_number)

    if session is not None:
        session = XFormsSession.get(session._id)
        if session.is_open:
            session.end(False)
            session.save()

    metadata = MessageMetadata(
        workflow=WORKFLOW_KEYWORD,
        xforms_session_couch_id=session._id if session else None,
    )

    if msg:
        add_msg_tags(msg, metadata)

    if error_occurred and verified_number is not None and send_response:
        send_sms_to_verified_number(verified_number, error_msg, metadata=metadata)

    return not error_occurred
Example #8
0
def handle_structured_sms(
    survey_keyword, survey_keyword_action, contact, verified_number, text, send_response=False, msg=None
):
    domain = contact.domain
    contact_doc_type = contact.doc_type
    contact_id = contact._id

    text = text.strip()
    if survey_keyword.delimiter is not None:
        args = text.split(survey_keyword.delimiter)
    else:
        args = text.split()

    keyword = args[0].strip().upper()

    error_occurred = False
    error_msg = None
    session = None

    try:
        # Start the session
        form = Form.get_form(survey_keyword_action.form_unique_id)
        app = form.get_app()
        module = form.get_module()

        if contact_doc_type == "CommCareCase":
            case_id = contact_id
        else:
            # TODO: Need a way to choose the case when it's a user that's playing the form
            case_id = None

        session, responses = start_session(domain, contact, app, module, form, case_id=case_id, yield_responses=True)
        session.workflow = WORKFLOW_KEYWORD
        session.save()
        if msg is not None:
            msg.workflow = WORKFLOW_KEYWORD
            msg.xforms_session_couch_id = session._id
            msg.save()
        assert len(responses) > 0, "There should be at least one response."

        current_question = responses[-1]
        form_complete = is_form_complete(current_question)

        if not form_complete:
            if survey_keyword_action.use_named_args:
                # Arguments in the sms are named
                xpath_answer = {}  # Dictionary of {xpath : answer}
                for answer in args[1:]:
                    answer = answer.strip()
                    answer_upper = answer.upper()
                    if survey_keyword_action.named_args_separator is not None:
                        # A separator is used for naming arguments; for example, the "=" in "register name=joe age=25"
                        answer_parts = answer.partition(survey_keyword_action.named_args_separator)
                        if answer_parts[1] != survey_keyword_action.named_args_separator:
                            raise StructuredSMSException(
                                response_text="ERROR: Expected name and value to be joined by '%(separator)s'"
                                % {"separator": survey_keyword_action.named_args_separator}
                            )
                        else:
                            arg_name = answer_parts[0].upper().strip()
                            xpath = survey_keyword_action.named_args.get(arg_name, None)
                            if xpath is not None:
                                if xpath in xpath_answer:
                                    raise StructuredSMSException(
                                        response_text="ERROR: More than one answer found for '%(arg_name)s'"
                                        % {"arg_name": arg_name}
                                    )

                                xpath_answer[xpath] = answer_parts[2].strip()
                            else:
                                # Ignore unexpected named arguments
                                pass
                    else:
                        # No separator is used for naming arguments; for example, "update a100 b34 c5"
                        matches = 0
                        for k, v in survey_keyword_action.named_args.items():
                            if answer_upper.startswith(k):
                                matches += 1
                                if matches > 1:
                                    raise StructuredSMSException(
                                        response_text="ERROR: More than one question matches '%(answer)s'"
                                        % {"answer": answer}
                                    )

                                if v in xpath_answer:
                                    raise StructuredSMSException(
                                        response_text="ERROR: More than one answer found for '%(named_arg)s'"
                                        % {"named_arg": k}
                                    )

                                xpath_answer[v] = answer[len(k) :].strip()

                        if matches == 0:
                            # Ignore unexpected named arguments
                            pass

                # Go through each question in the form, answering only the questions that the sms has answers for
                while not form_complete:
                    if current_question.is_error:
                        raise StructuredSMSException(
                            response_text=(current_question.text_prompt or "ERROR: Internal server error")
                        )

                    xpath = current_question.event._dict["binding"]
                    if xpath in xpath_answer:
                        valid, answer, _error_msg = validate_answer(current_question.event, xpath_answer[xpath])
                        if not valid:
                            raise StructuredSMSException(response_text=_error_msg)
                        responses = _get_responses(
                            domain,
                            contact_id,
                            answer,
                            yield_responses=True,
                            session_id=session.session_id,
                            update_timestamp=False,
                        )
                    else:
                        responses = _get_responses(
                            domain,
                            contact_id,
                            "",
                            yield_responses=True,
                            session_id=session.session_id,
                            update_timestamp=False,
                        )

                    current_question = responses[-1]
                    if is_form_complete(current_question):
                        form_complete = True
            else:
                # Arguments in the sms are not named; pass each argument to each question in order
                for answer in args[1:]:
                    if form_complete:
                        # Form is complete, ignore remaining answers
                        break

                    if current_question.is_error:
                        raise StructuredSMSException(
                            response_text=(current_question.text_prompt or "ERROR: Internal server error")
                        )

                    valid, answer, _error_msg = validate_answer(current_question.event, answer.strip())
                    if not valid:
                        raise StructuredSMSException(response_text=_error_msg)

                    responses = _get_responses(
                        domain,
                        contact_id,
                        answer,
                        yield_responses=True,
                        session_id=session.session_id,
                        update_timestamp=False,
                    )
                    current_question = responses[-1]
                    form_complete = is_form_complete(current_question)

                # If the form isn't finished yet but we're out of arguments, try to leave each remaining question blank and continue
                while not form_complete:
                    responses = _get_responses(
                        domain,
                        contact_id,
                        "",
                        yield_responses=True,
                        session_id=session.session_id,
                        update_timestamp=False,
                    )
                    current_question = responses[-1]

                    if current_question.is_error:
                        raise StructuredSMSException(
                            response_text=(current_question.text_prompt or "ERROR: Internal server error")
                        )

                    if is_form_complete(current_question):
                        form_complete = True
    except StructuredSMSException as sse:
        error_occurred = True
        error_msg = sse.response_text
    except Exception:
        logging.exception(
            "Could not process structured sms for contact %s, domain %s, keyword %s" % (contact_id, domain, keyword)
        )
        error_occurred = True
        error_msg = "ERROR: Internal server error"

    if session is not None:
        session = XFormsSession.get(session._id)
        if session.is_open:
            session.end(False)
            session.save()

    message_tags = {
        "workflow": WORKFLOW_KEYWORD,
        "xforms_session_couch_id": session._id if session is not None else None,
    }

    if msg is not None:
        msg.workflow = message_tags["workflow"]
        msg.xforms_session_couch_id = message_tags["xforms_session_couch_id"]
        msg.save()

    if error_occurred and verified_number is not None and send_response:
        send_sms_to_verified_number(verified_number, error_msg, **message_tags)
Example #9
0
            xpath_arg = None
            if survey_keyword_action.use_named_args:
                xpath_arg = \
                    {v: k for k, v in survey_keyword_action.named_args.items()}
            field_name = get_question_id(sse.xformsresponse, xpath_arg)
            error_msg = get_message(MSG_FIELD_DESCRIPTOR, verified_number,
                (field_name,))
        error_msg = "%s%s" % (error_msg, sse.response_text)
    except Exception:
        notify_exception(None, message=("Could not process structured sms for"
            "contact %s, domain %s, keyword %s" % (contact_id, domain, keyword)))
        error_occurred = True
        error_msg = get_message(MSG_TOUCHFORMS_ERROR, verified_number)

    if session is not None:
        session = XFormsSession.get(session._id)
        if session.is_open:
            session.end(False)
            session.save()

    metadata = MessageMetadata(
        workflow=WORKFLOW_KEYWORD,
        xforms_session_couch_id=session._id if session else None,
    )

    if msg:
        add_msg_tags(msg, metadata)

    if error_occurred and verified_number is not None and send_response:
        send_sms_to_verified_number(verified_number, error_msg, metadata=metadata)
Example #10
0
def structured_sms_handler(verified_number, text):

    # Circular Import
    from corehq.apps.reminders.models import SurveyKeyword, FORM_TYPE_ALL_AT_ONCE

    text = text.strip()
    if text == "":
        return False
    for survey_keyword in SurveyKeyword.get_all(verified_number.domain):
        if survey_keyword.form_type == FORM_TYPE_ALL_AT_ONCE:
            if survey_keyword.delimiter is not None:
                args = text.split(survey_keyword.delimiter)
            else:
                args = text.split()

            keyword = args[0].strip().upper()
            if keyword != survey_keyword.keyword.upper():
                continue

            try:
                error_occurred = False
                error_msg = ""
                form_complete = False

                # Close any open sessions
                close_open_sessions(verified_number.domain,
                                    verified_number.owner_id)

                # Start the session
                form = Form.get_form(survey_keyword.form_unique_id)
                app = form.get_app()
                module = form.get_module()

                if verified_number.owner_doc_type == "CommCareCase":
                    case_id = verified_number.owner_id
                else:
                    #TODO: Need a way to choose the case when it's a user that's playing the form
                    case_id = None

                session, responses = start_session(verified_number.domain,
                                                   verified_number.owner,
                                                   app,
                                                   module,
                                                   form,
                                                   case_id=case_id,
                                                   yield_responses=True)
                assert len(
                    responses) > 0, "There should be at least one response."

                current_question = responses[-1]
                form_complete = is_form_complete(current_question)

                if not form_complete:
                    if survey_keyword.use_named_args:
                        # Arguments in the sms are named
                        xpath_answer = {}  # Dictionary of {xpath : answer}
                        for answer in args[1:]:
                            answer = answer.strip()
                            answer_upper = answer.upper()
                            if survey_keyword.named_args_separator is not None:
                                # A separator is used for naming arguments; for example, the "=" in "register name=joe age=25"
                                answer_parts = answer.partition(
                                    survey_keyword.named_args_separator)
                                if answer_parts[
                                        1] != survey_keyword.named_args_separator:
                                    error_occurred = True
                                    error_msg = "ERROR: Expected name and value to be joined by" + (
                                        " '%s'" %
                                        survey_keyword.named_args_separator)
                                    break
                                else:
                                    arg_name = answer_parts[0].upper().strip()
                                    xpath = survey_keyword.named_args.get(
                                        arg_name, None)
                                    if xpath is not None:
                                        if xpath in xpath_answer:
                                            error_occurred = True
                                            error_msg = "ERROR: More than one answer found for" + (
                                                " '%s'" % arg_name)
                                            break

                                        xpath_answer[xpath] = answer_parts[
                                            2].strip()
                                    else:
                                        # Ignore unexpected named arguments
                                        pass
                            else:
                                # No separator is used for naming arguments; for example, "update a100 b34 c5"
                                matches = 0
                                for k, v in survey_keyword.named_args.items():
                                    if answer_upper.startswith(k):
                                        matches += 1
                                        if matches > 1:
                                            error_occurred = True
                                            error_msg = "ERROR: More than one question matches" + (
                                                " '%s'" % answer)
                                            break

                                        if v in xpath_answer:
                                            error_occurred = True
                                            error_msg = "ERROR: More than one answer found for" + (
                                                " '%s'" % k)
                                            break

                                        xpath_answer[v] = answer[
                                            len(k):].strip()

                                if matches == 0:
                                    # Ignore unexpected named arguments
                                    pass

                            if error_occurred:
                                break

                        # Go through each question in the form, answering only the questions that the sms has answers for
                        while not form_complete and not error_occurred:
                            if current_question.is_error:
                                error_occurred = True
                                error_msg = current_question.text_prompt or "ERROR: Internal server error"
                                break

                            xpath = current_question.event._dict["binding"]
                            if xpath in xpath_answer:
                                valid, answer, _error_msg = validate_answer(
                                    current_question.event,
                                    xpath_answer[xpath])
                                if not valid:
                                    error_occurred = True
                                    error_msg = "ERROR: " + _error_msg
                                    break
                                responses = _get_responses(
                                    verified_number.domain,
                                    verified_number.owner_id,
                                    answer,
                                    yield_responses=True)
                            else:
                                responses = _get_responses(
                                    verified_number.domain,
                                    verified_number.owner_id,
                                    "",
                                    yield_responses=True)

                            current_question = responses[-1]
                            if is_form_complete(current_question):
                                form_complete = True
                    else:
                        # Arguments in the sms are not named; pass each argument to each question in order
                        for answer in args[1:]:
                            if form_complete:
                                # Form is complete, ignore remaining answers
                                break

                            if current_question.is_error:
                                error_occurred = True
                                error_msg = current_question.text_prompt or "ERROR: Internal server error"
                                break

                            valid, answer, _error_msg = validate_answer(
                                current_question.event, answer.strip())
                            if not valid:
                                error_occurred = True
                                error_msg = "ERROR: " + _error_msg
                                break

                            responses = _get_responses(
                                verified_number.domain,
                                verified_number.owner_id,
                                answer,
                                yield_responses=True)
                            current_question = responses[-1]
                            form_complete = is_form_complete(current_question)

                        # If the form isn't finished yet but we're out of arguments, try to leave each remaining question blank and continue
                        while not form_complete and not error_occurred:
                            responses = _get_responses(
                                verified_number.domain,
                                verified_number.owner_id,
                                "",
                                yield_responses=True)
                            current_question = responses[-1]

                            if current_question.is_error:
                                error_occurred = True
                                error_msg = current_question.text_prompt or "ERROR: Internal server error"

                            if is_form_complete(current_question):
                                form_complete = True
            except Exception:
                logging.exception(
                    "Could not process structured sms for verified number %s, domain %s, keyword %s"
                    % (verified_number._id, verified_number.domain, keyword))
                error_occurred = True
                error_msg = "ERROR: Internal server error"

            if error_occurred:
                send_sms_to_verified_number(verified_number, error_msg)

            if error_occurred or not form_complete:
                session = XFormsSession.get(session._id)
                session.end(False)
                session.save()

            return True

    return False