Beispiel #1
0
def _update_case(domain, case_id, server_modified_on, last_visit_date=None):
    accessors = CaseAccessors(domain)
    case = accessors.get_case(case_id)
    case.server_modified_on = server_modified_on
    if last_visit_date:
        set_case_property_directly(case, 'last_visit_date', last_visit_date.strftime('%Y-%m-%d'))
    _save_case(domain, case)
Beispiel #2
0
    def test_match_days_before(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_BEFORE,
                rule=self.rule2,
            )
            # When the case property doesn't exist, it should not match
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-10-01')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2016-01-02')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2016-01-31')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2016-02-01')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2016-03-01')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
    def test_phone_number_already_in_use(self):
        case1 = CommCareCase(
            domain='case-phone-number-test',
            name='TEST1'
        )
        set_case_property_directly(case1, 'contact_phone_number', '99987658765')
        set_case_property_directly(case1, 'contact_phone_number_is_verified', '1')
        case1.save()

        case2 = CommCareCase(
            domain='case-phone-number-test',
            name='TEST2'
        )
        set_case_property_directly(case2, 'contact_phone_number', '99987698769')
        set_case_property_directly(case2, 'contact_phone_number_is_verified', '1')
        case2.save()

        self.assertIsNotNone(self.get_case_verified_number(case1))
        self.assertIsNotNone(self.get_case_verified_number(case2))

        set_case_property_directly(case2, 'contact_phone_number', '99987658765')
        case2.save()
        self.assertIsNotNone(self.get_case_verified_number(case1))
        self.assertIsNone(self.get_case_verified_number(case2))

        case2.delete()
        self.get_case_verified_number(case1).delete()
        case1.delete()
def _update_case(domain, case_id, server_modified_on, last_visit_date=None):
    accessors = CaseAccessors(domain)
    case = accessors.get_case(case_id)
    case.server_modified_on = server_modified_on
    if last_visit_date:
        set_case_property_directly(case, 'last_visit_date', last_visit_date.strftime('%Y-%m-%d'))
    if should_use_sql_backend(domain):
        CaseAccessorSQL.save_case(case)
    else:
        # can't call case.save() since it overrides the server_modified_on property
        CommCareCase.get_db().save_doc(case.to_json())
Beispiel #5
0
    def test_match_has_value(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property3',
                match_type=AutomaticUpdateRuleCriteria.MATCH_HAS_VALUE,
                rule=self.rule2,
            )
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property3', 'x')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property3', '')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
Beispiel #6
0
    def test_date_case_properties_for_inequality(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property1',
                property_value='2016-02-24',
                match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
                rule=self.rule2,
            )

            set_case_property_directly(case, 'property1', '2016-02-24')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property1', '2016-02-25')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
    def test_case_soft_delete(self):
        case = CommCareCase(
            domain='case-phone-number-test',
            name='TEST1'
        )
        set_case_property_directly(case, 'contact_phone_number', '99987658765')
        set_case_property_directly(case, 'contact_phone_number_is_verified', '1')
        case.save()
        self.assertIsNotNone(self.get_case_verified_number(case))

        case.doc_type += '-Deleted'
        case.save()
        self.assertIsNone(self.get_case_verified_number(case))
        case.delete()
Beispiel #8
0
    def test_match_not_equal(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property2',
                property_value='value2',
                match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
                rule=self.rule2,
            )
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property2', 'value2')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property2', 'x')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
    def test_match_equal(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            self.rule2.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='property1',
                    property_value='value1',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                ),
            ]
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property1', 'x')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property1', 'value1')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
Beispiel #10
0
    def test_date_case_properties_for_equality(self):
        """
        Date case properties are automatically converted from string to date
        when fetching from the db, so here we want to make sure this doesn't
        interfere with our ability to compare dates for equality.
        """
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property1',
                property_value='2016-02-24',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=self.rule2,
            )

            set_case_property_directly(case, 'property1', '2016-02-24')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property1', '2016-02-25')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
Beispiel #11
0
    def test_match_days_after(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
                rule=self.rule2,
            )
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-30')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-03')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-02')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-11-01')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
    def test_match_days_since(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            self.rule2.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='last_visit_date',
                    property_value='30',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
                ),
            ]
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-30')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-03')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-02')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-11-01')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
Beispiel #13
0
    def setUpClass(cls):
        cls.domain = 'sms-chat-history-test-domain'
        cls.domain_obj = Domain(name=cls.domain)
        cls.domain_obj.save()

        cls.contact1 = CommCareCase(domain=cls.domain, name='test-case')
        set_case_property_directly(cls.contact1, 'custom_name', 'custom-name')
        cls.contact1.save()

        cls.contact2 = CommCareCase(domain='another-domain')
        cls.contact2.save()

        cls.contact3 = CommCareUser.create(cls.domain, 'user1', '123')

        cls.chat_user = CommCareUser.create(cls.domain, 'user2', '123')
        cls.chat_user.first_name = 'Sam'
        cls.chat_user.save()

        cls.outgoing_from_system = SMS.objects.create(
            domain=cls.domain,
            direction=OUTGOING,
            date=datetime(2016, 2, 18, 0, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='Remember your appointment tomorrow',
            chat_user_id=None,
            processed=True,
            xforms_session_couch_id=None,
            invalid_survey_response=False,
        )

        cls.outgoing_not_processed = SMS.objects.create(
            domain=cls.domain,
            direction=OUTGOING,
            date=datetime(2016, 2, 18, 1, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='Remember your appointment next week',
            chat_user_id=None,
            processed=False,
            xforms_session_couch_id=None,
            invalid_survey_response=False,
        )

        cls.outgoing_from_chat = SMS.objects.create(
            domain=cls.domain,
            direction=OUTGOING,
            date=datetime(2016, 2, 18, 2, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='How are you?',
            chat_user_id=cls.chat_user.get_id,
            processed=True,
            xforms_session_couch_id=None,
            invalid_survey_response=False,
        )

        cls.incoming_not_processed = SMS.objects.create(
            domain=cls.domain,
            direction=INCOMING,
            date=datetime(2016, 2, 18, 3, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='Good',
            chat_user_id=None,
            processed=False,
            xforms_session_couch_id=None,
            invalid_survey_response=False,
        )

        cls.incoming_survey_answer1 = SMS.objects.create(
            domain=cls.domain,
            direction=INCOMING,
            date=datetime(2016, 2, 18, 4, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='x',
            chat_user_id=None,
            processed=True,
            xforms_session_couch_id='session-id',
            invalid_survey_response=True,
        )

        cls.outgoing_survey_response1 = SMS.objects.create(
            domain=cls.domain,
            direction=OUTGOING,
            date=datetime(2016, 2, 18, 5, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='Invalid Response',
            chat_user_id=None,
            processed=True,
            xforms_session_couch_id='session-id',
            invalid_survey_response=True,
        )

        cls.incoming_survey_answer2 = SMS.objects.create(
            domain=cls.domain,
            direction=INCOMING,
            date=datetime(2016, 2, 18, 6, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='1',
            chat_user_id=None,
            processed=True,
            xforms_session_couch_id='session-id',
            invalid_survey_response=False,
        )

        cls.outgoing_survey_response2 = SMS.objects.create(
            domain=cls.domain,
            direction=OUTGOING,
            date=datetime(2016, 2, 18, 7, 0),
            couch_recipient_doc_type=cls.contact1.doc_type,
            couch_recipient=cls.contact1.get_id,
            text='Thank you for completing the survey',
            chat_user_id=None,
            processed=True,
            xforms_session_couch_id='session-id',
            invalid_survey_response=False,
        )
    def test_all_inbound(self):
        # Mobile worker creates a case
        incoming("999123", "reg", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Participant ID")
        incoming("999123", "pid1234", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        incoming("999123", "1", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "participant_id", "pid1234")
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        self.assertFormQuestionEquals(form, "external_id", "pid1234")
        case = self.get_case("pid1234")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "name", "pid1234")
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Mobile worker modifies a case
        incoming("999123", "mod pid1234", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        incoming("999123", "b", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_b")
        case = self.get_case("pid1234")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "arm", "arm_b")

        # now take the case away from the user
        self.update_case_owner(case, self.user3)
        case = self.get_case("pid1234")

        # then they should no longer have access
        incoming("999123", "mod pid1234", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_CASE_NOT_FOUND))

        # now add access back via parent connection
        self.add_parent_access(self.user1, case)
        incoming("999123", "mod pid1234", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        incoming("999123", "a", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        case = self.get_case("pid1234")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        form = self.get_last_form_submission()

        # Bad external id
        incoming("999123", "mod pid1235", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_CASE_NOT_FOUND))
        self.assertNoNewSubmission(form)

        # No external id
        incoming("999123", "mod", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_MISSING_EXTERNAL_ID))
        self.assertNoNewSubmission(form)

        # Test validation on all fields
        incoming("999123", "Validation_Test", "TEST")
        session = self.get_open_session(self.user1)
        
        sms = self.assertLastOutboundSMSEquals(self.user1, "text")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "ab", "TEST")
        self.assertTrue(sms.invalid_survey_response)
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, 'Expected "abc"')
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "abc", "TEST")
        self.assertFalse(sms.invalid_survey_response)
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "single select 1:a, 2:b, 3:c, 4:d.")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "x", "TEST")
        self.assertTrue(sms.invalid_survey_response)
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s single select 1:a, 2:b, 3:c, 4:d." % get_message(MSG_INVALID_CHOICE))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "5", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s single select 1:a, 2:b, 3:c, 4:d." % get_message(MSG_CHOICE_OUT_OF_RANGE))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "2", "TEST")
        self.assertFalse(sms.invalid_survey_response)
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "multi select 1:a, 2:b, 3:c, 4:d.")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s multi select 1:a, 2:b, 3:c, 4:d." % get_message(MSG_FIELD_REQUIRED))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "2 x", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s multi select 1:a, 2:b, 3:c, 4:d." % get_message(MSG_INVALID_CHOICE))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "1 5", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s multi select 1:a, 2:b, 3:c, 4:d." % get_message(MSG_INVALID_CHOICE))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "1 c", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "int")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "x", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s int" % get_message(MSG_INVALID_INT))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "50", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "float")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "x", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s float" % get_message(MSG_INVALID_FLOAT))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "21.3", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)
        
        sms = self.assertLastOutboundSMSEquals(self.user1, "long")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "x", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s long" % get_message(MSG_INVALID_LONG))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "-100", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "date")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "x", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s date" % get_message(MSG_INVALID_DATE))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "20140101", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "time")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "x", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s time" % get_message(MSG_INVALID_TIME))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "2500", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = self.assertLastOutboundSMSEquals(self.user1, "%s time" % get_message(MSG_INVALID_TIME))
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        sms = incoming("999123", "2345", "TEST")
        self.assertMetadataEqual(sms, session._id, WORKFLOW_KEYWORD)

        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "b")
        self.assertFormQuestionEquals(form, "q_multi_select", "a c")
        self.assertFormQuestionEquals(form, "q_int", 50, cast=int)
        self.assertFormQuestionEquals(form, "q_float", 21.3, cast=float)
        self.assertFormQuestionEquals(form, "q_long", -100, cast=long)
        self.assertFormQuestionEquals(form, "q_date", date(2014, 1, 1))
        self.assertFormQuestionEquals(form, "q_time", time(23, 45), cast=time_parser)

        # Mobile worker creates a case via structured sms
        incoming("999123", "reg_ss pid1235 1", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your registration submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "participant_id", "pid1235")
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        self.assertFormQuestionEquals(form, "external_id", "pid1235")
        case = self.get_case("pid1235")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "name", "pid1235")
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Mobile worker modifies a case
        incoming("999123", "mod_ss pid1235 b", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your modification submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_b")
        case = self.get_case("pid1235")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "arm", "arm_b")

        # Bad external id
        incoming("999123", "mod_ss pid1236", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_CASE_NOT_FOUND))
        self.assertNoNewSubmission(form)

        # No external id
        incoming("999123", "mod_ss", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_MISSING_EXTERNAL_ID))
        self.assertNoNewSubmission(form)

        def get_field_and_message(field_name, msg_id):
            msg1 = get_message(MSG_FIELD_DESCRIPTOR,
                context=(field_name,))
            msg2 = get_message(msg_id)
            return "%s%s" % (msg1, msg2)

        # Test validation on all fields from structured sms: positional args
        incoming("999123", "validation_test_ss_1 ab 2 c 50 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, 'Expected "abc"')
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc x c 50 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_single_select", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 5 c 50 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_single_select", MSG_CHOICE_OUT_OF_RANGE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 x 50 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_multi_select", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 5 50 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_multi_select", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c x 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_int", MSG_INVALID_INT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c 50 x -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_float", MSG_INVALID_FLOAT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c 50 21.3 x 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_long", MSG_INVALID_LONG))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c 50 21.3 -100 x 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_date", MSG_INVALID_DATE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c 50 21.3 -100 20140101 x", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_time", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c 50 21.3 -100 20140101 2500", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_time", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_1 abc 2 c 50 21.3 -100 20140101 2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "b")
        self.assertFormQuestionEquals(form, "q_multi_select", "c")
        self.assertFormQuestionEquals(form, "q_int", 50, cast=int)
        self.assertFormQuestionEquals(form, "q_float", 21.3, cast=float)
        self.assertFormQuestionEquals(form, "q_long", -100, cast=long)
        self.assertFormQuestionEquals(form, "q_date", date(2014, 1, 1))
        self.assertFormQuestionEquals(form, "q_time", time(23, 45), cast=time_parser)

        # Test validation on all fields from structured sms: positional args with custom delimiter
        incoming("999123", "validation_test_ss_2,ab,2,1 c,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, 'Expected "abc"')
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,x,1 c,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_single_select", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,5,1 c,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_single_select", MSG_CHOICE_OUT_OF_RANGE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 x,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_multi_select", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 5,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_multi_select", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_multi_select", MSG_FIELD_REQUIRED))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,x,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_int", MSG_INVALID_INT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,50,x,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_float", MSG_INVALID_FLOAT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,50,21.3,x,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_long", MSG_INVALID_LONG))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,50,21.3,-100,x,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_date", MSG_INVALID_DATE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,50,21.3,-100,20140101,x", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_time", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,50,21.3,-100,20140101,2500", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("q_time", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_2,abc,2,1 c,50,21.3,-100,20140101,2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "b")
        self.assertFormQuestionEquals(form, "q_multi_select", "a c")
        self.assertFormQuestionEquals(form, "q_int", 50, cast=int)
        self.assertFormQuestionEquals(form, "q_float", 21.3, cast=float)
        self.assertFormQuestionEquals(form, "q_long", -100, cast=long)
        self.assertFormQuestionEquals(form, "q_date", date(2014, 1, 1))
        self.assertFormQuestionEquals(form, "q_time", time(23, 45), cast=time_parser)

        # Test validation on all fields from structured sms: named args with custom delimiter
        incoming("999123", "validation_test_ss_3,arg1ab,arg22,arg31 c,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, 'Expected "abc"')
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg2x,arg31 c,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG2", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg25,arg31 c,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG2", MSG_CHOICE_OUT_OF_RANGE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 x,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG3", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 5,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG3", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg3,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG3", MSG_FIELD_REQUIRED))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg4x,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG4", MSG_INVALID_INT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg450,arg5x,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG5", MSG_INVALID_FLOAT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg450,arg521.3,arg6x,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG6", MSG_INVALID_LONG))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg450,arg521.3,arg6-100,arg7x,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG7", MSG_INVALID_DATE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg450,arg521.3,arg6-100,arg720140101,arg8x", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG8", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg450,arg521.3,arg6-100,arg720140101,arg82500", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG8", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_3,arg1abc,arg22,arg31 c,arg450,arg521.3,arg6-100,arg720140101,arg82345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "b")
        self.assertFormQuestionEquals(form, "q_multi_select", "a c")
        self.assertFormQuestionEquals(form, "q_int", 50, cast=int)
        self.assertFormQuestionEquals(form, "q_float", 21.3, cast=float)
        self.assertFormQuestionEquals(form, "q_long", -100, cast=long)
        self.assertFormQuestionEquals(form, "q_date", date(2014, 1, 1))
        self.assertFormQuestionEquals(form, "q_time", time(23, 45), cast=time_parser)

        # Test validation on all fields from structured sms: named args with custom delimiter and joining character
        incoming("999123", "validation_test_ss_4,arg1=ab,arg2=2,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, 'Expected "abc"')
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=x,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG2", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=5,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG2", MSG_CHOICE_OUT_OF_RANGE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 x,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG3", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 5,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG3", MSG_INVALID_CHOICE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG3", MSG_FIELD_REQUIRED))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=x,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG4", MSG_INVALID_INT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=50,arg5=x,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG5", MSG_INVALID_FLOAT))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=50,arg5=21.3,arg6=x,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG6", MSG_INVALID_LONG))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=x,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG7", MSG_INVALID_DATE))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=x", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG8", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2500", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARG8", MSG_INVALID_TIME))
        self.assertNoNewSubmission(form)
        incoming("999123", "validation_test_ss_4,arg1=abc,arg2=2,arg3=1 c,arg4=50,arg5=21.3,arg6=-100,arg7=20140101,arg8=2345", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "b")
        self.assertFormQuestionEquals(form, "q_multi_select", "a c")
        self.assertFormQuestionEquals(form, "q_int", 50, cast=int)
        self.assertFormQuestionEquals(form, "q_float", 21.3, cast=float)
        self.assertFormQuestionEquals(form, "q_long", -100, cast=long)
        self.assertFormQuestionEquals(form, "q_date", date(2014, 1, 1))
        self.assertFormQuestionEquals(form, "q_time", time(23, 45), cast=time_parser)

        # Test leaving fields blank via structured sms
        incoming("999123", "validation_test_ss_4,arg1=abc,arg3=1 c", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "")
        self.assertFormQuestionEquals(form, "q_multi_select", "a c")
        self.assertFormQuestionEquals(form, "q_int", "")
        self.assertFormQuestionEquals(form, "q_float", "")
        self.assertFormQuestionEquals(form, "q_long", "")
        self.assertFormQuestionEquals(form, "q_date", "")
        self.assertFormQuestionEquals(form, "q_time", "")

        incoming("999123", "validation_test_ss_1 abc b c", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "q_text", "abc")
        self.assertFormQuestionEquals(form, "q_single_select", "b")
        self.assertFormQuestionEquals(form, "q_multi_select", "c")
        self.assertFormQuestionEquals(form, "q_int", "")
        self.assertFormQuestionEquals(form, "q_float", "")
        self.assertFormQuestionEquals(form, "q_long", "")
        self.assertFormQuestionEquals(form, "q_date", "")
        self.assertFormQuestionEquals(form, "q_time", "")

        incoming("999123", "mod_ss_2,pid1235", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("arm", MSG_FIELD_REQUIRED))
        self.assertNoNewSubmission(form)

        incoming("999123", "mod_ss_2,pid1235,", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("arm", MSG_FIELD_REQUIRED))
        self.assertNoNewSubmission(form)

        incoming("999123", "mod_ss_3,pid1235,arm=", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_field_and_message("ARM", MSG_FIELD_REQUIRED))
        self.assertNoNewSubmission(form)

        incoming("999123", "mod_ss_3,pid1235,arm a", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_EXPECTED_NAMED_ARGS_SEPARATOR, context=("=",)))
        self.assertNoNewSubmission(form)

        incoming("999123", "mod_ss_3,pid1235,arm=a,arm=b", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_MULTIPLE_ANSWERS_FOUND, context=("ARM",)))
        self.assertNoNewSubmission(form)

        incoming("999123", "mod_ss_3 ,  pid1235  ,  arm = a", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Thank you for your modification submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        case = self.get_case("pid1235")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Test global keywords
        incoming("999123", "#start unknownkeyword", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_KEYWORD_NOT_FOUND, context=("UNKNOWNKEYWORD",)))
        self.assertNoNewSubmission(form)

        incoming("999123", "#start", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_START_KEYWORD_USAGE, context=("#START",)))
        self.assertNoNewSubmission(form)

        incoming("999123", "#unknown", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_UNKNOWN_GLOBAL_KEYWORD, context=("#UNKNOWN",)))
        self.assertNoNewSubmission(form)

        # Mobile worker creates a case
        incoming("999123", "#start reg", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Participant ID")
        incoming("999123", "pid1237", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        incoming("999123", "1", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "participant_id", "pid1237")
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        self.assertFormQuestionEquals(form, "external_id", "pid1237")
        case = self.get_case("pid1237")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "name", "pid1237")
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Mobile worker modifies a case
        incoming("999123", "#start mod pid1237", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        incoming("999123", "b", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_b")
        case = self.get_case("pid1237")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "arm", "arm_b")

        # Bad external id
        incoming("999123", "#start mod pid1240", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_CASE_NOT_FOUND))
        self.assertNoNewSubmission(form)

        # No external id
        incoming("999123", "#start mod", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, get_message(MSG_MISSING_EXTERNAL_ID))
        self.assertNoNewSubmission(form)

        # CURRENT keyword
        incoming("999123", "reg", "TEST")
        sms1 = self.assertLastOutboundSMSEquals(self.user1, "Enter Participant ID")
        incoming("999123", "#CURRENT", "TEST")
        sms2 = self.assertLastOutboundSMSEquals(self.user1, "Enter Participant ID")
        self.assertNotEqual(sms1.pk, sms2.pk)

        # STOP keyword
        session = self.get_open_session(self.user1)
        self.assertIsNotNone(session)
        incoming("999123", "#STOP", "TEST")
        session = self.get_open_session(self.user1)
        self.assertIsNone(session)
        self.assertNoNewSubmission(form)

        # One keyword overrides another
        incoming("999123", "reg", "TEST")
        sms1 = self.assertLastOutboundSMSEquals(self.user1, "Enter Participant ID")
        incoming("999123", "mod pid1237", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        self.assertNoNewSubmission(form)
        incoming("999123", "reg", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "%s Enter Study Arm 1:a, 2:b." % get_message(MSG_INVALID_CHOICE))
        incoming("999123", "a", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        case = self.get_case("pid1237")
        self.assertIsNotNone(case)
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Test initator filters
        case = self.get_case("pid1237")
        set_case_property_directly(case, "contact_phone_number", "999124")
        set_case_property_directly(case, "contact_phone_number_is_verified", "1")
        case.save()

        incoming("999123", "for_user", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "This message is for users")
        incoming("999123", "for_case", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Default SMS Response")

        incoming("999124", "for_case", "TEST")
        self.assertLastOutboundSMSEquals(case, "This message is for cases")
        incoming("999124", "for_user", "TEST")
        self.assertLastOutboundSMSEquals(case, "Default SMS Response")

        # Test form over sms for case
        incoming("999124", "mod", "TEST")
        self.assertLastOutboundSMSEquals(case, "Enter Study Arm 1:a, 2:b.")
        incoming("999123", "mod pid1237", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "Enter Study Arm 1:a, 2:b.")
        incoming("999124", "b", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_b")
        case = self.get_case("pid1237")
        self.assertCasePropertyEquals(case, "arm", "arm_b")

        incoming("999123", "a", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        case = self.get_case("pid1237")
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Test structured sms for case
        incoming("999124", "mod_ss 2", "TEST")
        self.assertLastOutboundSMSEquals(case, "Thank you for your modification submission.")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_b")
        case = self.get_case("pid1237")
        self.assertCasePropertyEquals(case, "arm", "arm_b")

        # Test Auth
        incoming("999122", "mod pid1237", "TEST")
        self.assertLastOutboundSMSEquals(self.user2, get_message(MSG_CASE_NOT_FOUND))

        # Test notifying others
        incoming("999124", "for_owner", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "This message is for the case owner")

        incoming("999124", "for_group", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "This message is for the group")
        self.assertLastOutboundSMSEquals(self.user2, "This message is for the group")

        case = self.get_case("pid1237")
        case.owner_id = self.group1._id
        case.save()
        incoming("999124", "for_owner", "TEST")
        self.assertLastOutboundSMSEquals(self.user1, "This message is for the case owner")
        self.assertLastOutboundSMSEquals(self.user2, "This message is for the case owner")

        # Test case sharing auth
        incoming("999122", "mod pid1237", "TEST")
        self.assertLastOutboundSMSEquals(self.user2, "Enter Study Arm 1:a, 2:b.")
        incoming("999122", "1", "TEST")
        form = self.get_last_form_submission()
        self.assertFormQuestionEquals(form, "arm", "arm_a")
        case = self.get_case("pid1237")
        self.assertCasePropertyEquals(case, "arm", "arm_a")

        # Test closing open sessions on an sms reply
        incoming("999122", "reg", "TEST")
        self.assertLastOutboundSMSEquals(self.user2, "Enter Participant ID")
        incoming("999122", "for_user", "TEST")
        self.assertLastOutboundSMSEquals(self.user2, "This message is for users")
        incoming("999122", "null", "TEST")
        self.assertLastOutboundSMSEquals(self.user2, "Default SMS Response")
    def test_and_criteria(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:

            self.rule2.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='last_visit_date',
                    property_value='30',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
                ),
                AutomaticUpdateRuleCriteria(
                    property_name='property1',
                    property_value='value1',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                ),
                AutomaticUpdateRuleCriteria(
                    property_name='property2',
                    property_value='value2',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
                ),
                AutomaticUpdateRuleCriteria(
                    property_name='property3',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_HAS_VALUE,
                ),
            ]

            set_case_property_directly(case, 'last_visit_date', '2015-11-01')
            set_case_property_directly(case, 'property1', 'value1')
            set_case_property_directly(case, 'property2', 'x')
            set_case_property_directly(case, 'property3', 'x')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-12-30')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'last_visit_date', '2015-11-01')
            set_case_property_directly(case, 'property1', 'x')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property1', 'value1')
            set_case_property_directly(case, 'property2', 'value2')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property2', 'x')
            set_case_property_directly(case, 'property3', '')
            self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

            set_case_property_directly(case, 'property3', 'x')
            self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))
    def test_case_phone_number_updates(self):
        case = CommCareCase(
            domain='case-phone-number-test',
            name='TEST1'
        )
        case.save()
        self.assertIsNone(self.get_case_verified_number(case))

        set_case_property_directly(case, 'contact_phone_number', '99987658765')
        case.save()
        self.assertIsNone(self.get_case_verified_number(case))

        set_case_property_directly(case, 'contact_phone_number_is_verified', '1')
        case.save()
        self.assertPhoneNumberDetails(case, '99987658765', None, None, _rev='1')
        _id = self.get_case_verified_number(case)._id

        set_case_property_directly(case, 'contact_phone_number', '99987698769')
        case.save()
        self.assertPhoneNumberDetails(case, '99987698769', None, None, _id=_id, _rev='2')

        set_case_property_directly(case, 'contact_backend_id', 'sms-backend')
        case.save()
        self.assertPhoneNumberDetails(case, '99987698769', 'sms-backend', None, _id=_id, _rev='3')

        set_case_property_directly(case, 'contact_ivr_backend_id', 'ivr-backend')
        case.save()
        self.assertPhoneNumberDetails(case, '99987698769', 'sms-backend', 'ivr-backend', _id=_id, _rev='4')

        # If nothing changes, the phone entry should not be saved
        case.save()
        self.assertTrue(self.get_case_verified_number(case)._rev.startswith('4-'))

        # If phone entry is ahead of the case in terms of contact_last_modified, no update should happen
        v = self.get_case_verified_number(case)
        v.contact_last_modified += timedelta(days=1)
        v.save()
        self.assertTrue(v._rev.startswith('5-'))

        set_case_property_directly(case, 'contact_phone_number', '99912341234')
        case.save()
        self.assertTrue(self.get_case_verified_number(case)._rev.startswith('5-'))

        self.get_case_verified_number(case).delete()
        case.delete()