def test_run_messaging_rule(self, task_patch):
        schedule = AlertSchedule.create_simple_alert(
            self.domain,
            SMSContent(message={'en': 'Hello'})
        )

        rule = create_empty_rule(self.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)

        rule.add_action(
            CreateScheduleInstanceActionDefinition,
            alert_schedule_id=schedule.schedule_id,
            recipients=(('Self', None),),
        )

        AutomaticUpdateRule.clear_caches(self.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)

        with create_case(self.domain, 'person') as case1, create_case(self.domain, 'person') as case2:
            run_messaging_rule(self.domain, rule.pk)
            self.assertEqual(task_patch.call_count, 2)
            task_patch.assert_has_calls(
                [
                    call(self.domain, case1.case_id, rule.pk),
                    call(self.domain, case2.case_id, rule.pk),
                ],
                any_order=True
            )
Exemplo n.º 2
0
    def test_pillow_processes_changes(self, find_duplicate_cases_mock):
        kafka_sec = get_topic_offset(topics.FORM_SQL)

        case1 = self.factory.create_case(case_name="foo",
                                         case_type=self.case_type,
                                         update={"age": 2})
        case2 = self.factory.create_case(case_name="foo",
                                         case_type=self.case_type,
                                         update={"age": 2})
        find_duplicate_cases_mock.return_value = [case1.case_id, case2.case_id]

        AutomaticUpdateRule.clear_caches(
            self.domain, AutomaticUpdateRule.WORKFLOW_DEDUPLICATE)

        new_kafka_sec = get_topic_offset(topics.FORM_SQL)
        self.pillow.process_changes(since=kafka_sec, forever=False)

        self._assert_case_duplicate_pair(case1.case_id, [case2.case_id])
        self._assert_case_duplicate_pair(case2.case_id, [case1.case_id])
        self.assertEqual(CaseDuplicate.objects.count(), 2)
        self.assertEqual(
            CommCareCase.objects.get_case(
                case1.case_id, self.domain).get_case_property('age'), '5')
        self.assertEqual(
            CommCareCase.objects.get_case(case1.case_id, self.domain).name,
            'Herman Miller')

        # The new changes present should not be processed by the pillow
        # processor, since they were updates from a duplicate action.
        with patch.object(CaseDeduplicationProcessor,
                          '_process_case_update') as p:
            self.pillow.process_changes(since=new_kafka_sec, forever=False)
            p.assert_not_called()
Exemplo n.º 3
0
    def test_get_rules_from_domain(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(
            rules)

        expected_case_types = [
            'test-case-type', 'test-case-type-2', 'test-case-type-3'
        ]
        actual_case_types = rules_by_case_type.keys()
        self.assertEqual(set(expected_case_types), set(actual_case_types))

        expected_rule_ids = [self.rule.pk, self.rule4.pk]
        actual_rule_ids = [
            rule.pk for rule in rules_by_case_type['test-case-type']
        ]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule2.pk, self.rule3.pk]
        actual_rule_ids = [
            rule.pk for rule in rules_by_case_type['test-case-type-2']
        ]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule5.pk]
        actual_rule_ids = [
            rule.pk for rule in rules_by_case_type['test-case-type-3']
        ]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))
    def setUp(self):
        self.now = datetime(2015, 10, 22, 0, 0)
        self.domain = 'auto-update-test'
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            ),
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='update_flag',
                property_value='Y',
            ),
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        case = CommCareCase(domain=self.domain, type='test-case-type')
        case.save()
        self.case_id = case.get_id
Exemplo n.º 5
0
def run_case_update_rules_for_domain(domain, now=None):
    now = now or datetime.utcnow()
    start_run = datetime.utcnow()
    all_rules = AutomaticUpdateRule.by_domain(domain)
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(all_rules)

    for case_type, rules in rules_by_case_type.iteritems():
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_id_chunks = AutomaticUpdateRule.get_case_ids(domain, case_type, boundary_date)

        for case_ids in case_id_chunks:
            for case in CaseAccessors(domain).iter_cases(case_ids):
                time_elapsed = datetime.utcnow() - start_run
                if time_elapsed.seconds > HALT_AFTER:
                    notify_error(
                        "Halting rule run for domain %s as it's been running for more than a day." % domain
                    )
                    return

                for rule in rules:
                    stop_processing = rule.apply_rule(case, now)
                    if stop_processing:
                        break

        for rule in rules:
            rule.last_run = now
            rule.save()
Exemplo n.º 6
0
    def post(self, request, *args, **kwargs):
        rule_form_valid = self.rule_form.is_valid()
        criteria_form_valid = self.criteria_form.is_valid()
        actions_form_valid = self.actions_form.is_valid()

        if self.read_only_mode:
            # Don't allow making changes to rules that have custom
            # criteria/actions unless the user has permission to
            return HttpResponseBadRequest()

        if rule_form_valid and criteria_form_valid and actions_form_valid:
            if not self.is_system_admin and (
                self.criteria_form.requires_system_admin_to_save or
                self.actions_form.requires_system_admin_to_save
            ):
                # Don't allow adding custom criteria/actions to rules
                # unless the user has permission to
                return HttpResponseBadRequest()

            with transaction.atomic():
                if self.initial_rule:
                    rule = self.initial_rule
                else:
                    rule = AutomaticUpdateRule(
                        domain=self.domain,
                        active=True,
                        workflow=AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
                    )

                rule.name = self.rule_form.cleaned_data['name']
                self.criteria_form.save_criteria(rule)
                self.actions_form.save_actions(rule)
            return HttpResponseRedirect(reverse(AutomaticUpdateRuleListView.urlname, args=[self.domain]))

        return self.get(request, *args, **kwargs)
Exemplo n.º 7
0
    def post(self, request, *args, **kwargs):
        rule_form_valid = self.rule_form.is_valid()
        criteria_form_valid = self.criteria_form.is_valid()
        actions_form_valid = self.actions_form.is_valid()

        if self.read_only_mode:
            # Don't allow making changes to rules that have custom
            # criteria/actions unless the user has permission to
            return HttpResponseBadRequest()

        if rule_form_valid and criteria_form_valid and actions_form_valid:
            if not self.is_system_admin and (
                self.criteria_form.requires_system_admin_to_save or
                self.actions_form.requires_system_admin_to_save
            ):
                # Don't allow adding custom criteria/actions to rules
                # unless the user has permission to
                return HttpResponseBadRequest()

            with transaction.atomic():
                if self.initial_rule:
                    rule = self.initial_rule
                else:
                    rule = AutomaticUpdateRule(
                        domain=self.domain,
                        active=True,
                        workflow=AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
                    )

                rule.name = self.rule_form.cleaned_data['name']
                self.criteria_form.save_criteria(rule)
                self.actions_form.save_actions(rule)
            return HttpResponseRedirect(reverse(AutomaticUpdateRuleListView.urlname, args=[self.domain]))

        return self.get(request, *args, **kwargs)
Exemplo n.º 8
0
 def _get_rules(self, domain, case_type):
     domain_rules = self.rules_by_domain.get(domain)
     if not domain_rules or domain_rules.expired():
         rules = AutomaticUpdateRule.by_domain_cached(domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)
         domain_rules = CaseRules(
             datetime.utcnow(), AutomaticUpdateRule.organize_rules_by_case_type(rules)
         )
         self.rules_by_domain[domain] = domain_rules
     return domain_rules.by_case_type.get(case_type, [])
    def test_timed_schedule_instance_creation(self, utcnow_patch):
        schedule = TimedSchedule.create_simple_daily_schedule(
            self.domain,
            TimedEvent(time=time(9, 0)),
            SMSContent(message={'en': 'Hello'})
        )

        rule = create_empty_rule(self.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)

        rule.add_criteria(
            MatchPropertyDefinition,
            property_name='start_sending',
            property_value='Y',
            match_type=MatchPropertyDefinition.MATCH_EQUAL,
        )

        rule.add_action(
            CreateScheduleInstanceActionDefinition,
            timed_schedule_id=schedule.schedule_id,
            recipients=(('CommCareUser', self.user.get_id),)
        )

        AutomaticUpdateRule.clear_caches(self.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)

        utcnow_patch.return_value = datetime(2017, 5, 1, 7, 0)
        with create_case(self.domain, 'person') as case:
            # Rule does not match, no instances created
            instances = get_case_timed_schedule_instances_for_schedule(case.case_id, schedule)
            self.assertEqual(instances.count(), 0)

            # Make the rule match. On the first iteration, the instance is created. On the second,
            # no new instance is created since it already exists.
            for minute in [1, 2]:
                utcnow_patch.return_value = datetime(2017, 5, 1, 7, minute)
                update_case(self.domain, case.case_id, case_properties={'start_sending': 'Y'})

                instances = get_case_timed_schedule_instances_for_schedule(case.case_id, schedule)
                self.assertEqual(instances.count(), 1)

                self.assertEqual(instances[0].case_id, case.case_id)
                self.assertEqual(instances[0].rule_id, rule.pk)
                self.assertEqual(instances[0].timed_schedule_id, schedule.schedule_id)
                self.assertEqual(instances[0].start_date, date(2017, 5, 1))
                self.assertEqual(instances[0].domain, self.domain)
                self.assertEqual(instances[0].recipient_type, 'CommCareUser')
                self.assertEqual(instances[0].recipient_id, self.user.get_id)
                self.assertEqual(instances[0].current_event_num, 0)
                self.assertEqual(instances[0].schedule_iteration_num, 1)
                self.assertEqual(instances[0].next_event_due, datetime(2017, 5, 1, 13, 0))
                self.assertTrue(instances[0].active)

            # Make the rule not match. Instance should no longer exist.
            utcnow_patch.return_value = datetime(2017, 5, 1, 7, 3)
            update_case(self.domain, case.case_id, case_properties={'start_sending': 'N'})
            instances = get_case_timed_schedule_instances_for_schedule(case.case_id, schedule)
            self.assertEqual(instances.count(), 0)
Exemplo n.º 10
0
    def test_boundary_date(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type-2'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))
Exemplo n.º 11
0
    def test_boundary_date(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(
            rules)

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type-2'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))
Exemplo n.º 12
0
    def test_domain_has_conditional_alerts(self):
        self.assertFalse(
            AutomaticUpdateRule.domain_has_conditional_alerts(self.domain))
        self.assertFalse(domain_has_reminders(self.domain))

        rule = create_empty_rule(self.domain,
                                 AutomaticUpdateRule.WORKFLOW_SCHEDULING)
        self.addCleanup(rule.delete)

        self.assertTrue(
            AutomaticUpdateRule.domain_has_conditional_alerts(self.domain))
        self.assertTrue(domain_has_reminders(self.domain))
Exemplo n.º 13
0
def run_case_update_rules_for_domain_and_db(domain, now, run_id, db=None):
    domain_obj = Domain.get_by_name(domain)
    max_allowed_updates = domain_obj.auto_case_update_limit or settings.MAX_RULE_UPDATES_IN_ONE_RUN
    start_run = datetime.utcnow()

    last_migration_check_time = None
    cases_checked = 0
    case_update_result = CaseRuleActionResult()

    all_rules = list(
        AutomaticUpdateRule.by_domain(
            domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE))
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(
        all_rules)

    for case_type, rules in six.iteritems(rules_by_case_type):
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_ids = list(
            AutomaticUpdateRule.get_case_ids(domain,
                                             case_type,
                                             boundary_date,
                                             db=db))

        for case in CaseAccessors(domain).iter_cases(case_ids):
            migration_in_progress, last_migration_check_time = check_data_migration_in_progress(
                domain, last_migration_check_time)

            time_elapsed = datetime.utcnow() - start_run
            if (time_elapsed.seconds > HALT_AFTER
                    or case_update_result.total_updates >= max_allowed_updates
                    or migration_in_progress):
                DomainCaseRuleRun.done(run_id,
                                       DomainCaseRuleRun.STATUS_HALTED,
                                       cases_checked,
                                       case_update_result,
                                       db=db)
                notify_error("Halting rule run for domain %s." % domain)
                return

            case_update_result.add_result(run_rules_for_case(case, rules, now))
            cases_checked += 1

    run = DomainCaseRuleRun.done(run_id,
                                 DomainCaseRuleRun.STATUS_FINISHED,
                                 cases_checked,
                                 case_update_result,
                                 db=db)

    if run.status == DomainCaseRuleRun.STATUS_FINISHED:
        for rule in all_rules:
            AutomaticUpdateRule.objects.filter(pk=rule.pk).update(last_run=now)
Exemplo n.º 14
0
    def post(self, request, *args, **kwargs):
        if self.async_response is not None:
            return self.async_response

        basic_info_form_valid = self.basic_info_form.is_valid()
        criteria_form_valid = self.criteria_form.is_valid()
        schedule_form_valid = self.schedule_form.is_valid()

        if self.read_only_mode:
            # Don't allow making changes to rules that have custom
            # criteria/actions unless the user has permission to
            return HttpResponseBadRequest()

        if basic_info_form_valid and criteria_form_valid and schedule_form_valid:
            if not self.is_system_admin and (
                self.criteria_form.requires_system_admin_to_save or
                self.schedule_form.requires_system_admin_to_save
            ):
                # Don't allow adding custom criteria/actions to rules
                # unless the user has permission to
                return HttpResponseBadRequest()

            if not self.can_use_inbound_sms and self.schedule_form.uses_sms_survey:
                return HttpResponseBadRequest(
                    "Cannot create or edit survey reminders because subscription "
                    "does not have access to inbound SMS"
                )

            with transaction.atomic():
                if self.rule:
                    rule = self.rule
                else:
                    rule = AutomaticUpdateRule(
                        domain=self.domain,
                        active=True,
                        workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                    )

                rule.name = self.basic_info_form.cleaned_data['name']
                self.criteria_form.save_criteria(rule)
                self.schedule_form.save_rule_action_and_schedule(rule)

            if not (
                self.new_reminders_migrator and
                self.schedule_form.cleaned_data['skip_running_rule_post_save']
            ):
                initiate_messaging_rule_run(rule.domain, rule.pk)
            return HttpResponseRedirect(reverse(ConditionalAlertListView.urlname, args=[self.domain]))

        return self.get(request, *args, **kwargs)
Exemplo n.º 15
0
    def test_auto_case_update_rules(self):
        from corehq.apps.data_interfaces.models import (
            AutomaticUpdateRule, AutomaticUpdateRuleCriteria,
            AutomaticUpdateAction)
        expected_object_counts = Counter({
            AutomaticUpdateRule: 1,
            AutomaticUpdateRuleCriteria: 1,
            AutomaticUpdateAction: 2,
        })
        register_cleanup(self, list(expected_object_counts), self.domain_name)

        pre_rule = AutomaticUpdateRule(
            domain=self.domain_name,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        pre_rule.save()
        pre_criteria = AutomaticUpdateRuleCriteria.objects.create(
            property_name='last_visit_date',
            property_value='30',
            match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
            rule=pre_rule,
        )
        pre_action_update = AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='update_flag',
            property_value='Y',
            rule=pre_rule,
        )
        pre_action_close = AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=pre_rule,
        )

        self._dump_and_load(expected_object_counts)

        post_rule = AutomaticUpdateRule.objects.get(pk=pre_rule.pk)
        post_criteria = AutomaticUpdateRuleCriteria.objects.get(
            pk=pre_criteria.pk)
        post_action_update = AutomaticUpdateAction.objects.get(
            pk=pre_action_update.pk)
        post_action_close = AutomaticUpdateAction.objects.get(
            pk=pre_action_close.pk)

        self.assertModelsEqual(
            [pre_rule, pre_criteria, pre_action_update, pre_action_close],
            [post_rule, post_criteria, post_action_update, post_action_close])
Exemplo n.º 16
0
    def test_get_rules_from_domain(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        expected_case_types = ['test-case-type', 'test-case-type-2']
        actual_case_types = rules_by_case_type.keys()
        self.assertEqual(set(expected_case_types), set(actual_case_types))

        expected_rule_ids = [self.rule.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule2.pk, self.rule3.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type-2']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))
Exemplo n.º 17
0
    def get_pagination_data(self, in_data):
        try:
            limit = int(in_data['limit'])
            page = int(in_data['page'])
        except (TypeError, KeyError, ValueError):
            return {
                'success': False,
                'error': _("Please provide pagination info."),
            }

        start = (page - 1) * limit
        stop = limit * page

        rules = AutomaticUpdateRule.by_domain(
            self.domain,
            AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
            active_only=False,
        )

        rule_page = rules.order_by('name')[start:stop]
        total = rules.count()

        return {
            'response': {
                'itemList': map(self._format_rule, rule_page),
                'total': total,
                'page': page,
            },
            'success': True,
        }
Exemplo n.º 18
0
 def response_data_cleanup(domain, new_plan_version):
     """
     Any active automatic case update rules should be deactivated.
     """
     try:
         AutomaticUpdateRule.by_domain(
             domain.name,
             AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
         ).update(active=False)
         AutomaticUpdateRule.clear_caches(
             domain.name, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE)
         return True
     except Exception:
         log_accounting_error("Failed to deactivate automatic update rules "
                              "for domain %s." % domain.name)
         return False
Exemplo n.º 19
0
def domain_has_reminders(domain):
    from corehq.apps.data_interfaces.models import AutomaticUpdateRule
    from corehq.messaging.scheduling.models import ScheduledBroadcast, ImmediateBroadcast

    return (AutomaticUpdateRule.domain_has_conditional_alerts(domain)
            or ScheduledBroadcast.domain_has_broadcasts(domain)
            or ImmediateBroadcast.domain_has_broadcasts(domain))
Exemplo n.º 20
0
def run_case_update_rules_for_domain(domain, now=None):
    now = now or datetime.utcnow()

    domain_rules = AutomaticUpdateRule.by_domain(
        domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE)
    all_rule_case_types = set(domain_rules.values_list('case_type', flat=True))

    for case_type in all_rule_case_types:
        run_record = DomainCaseRuleRun.objects.create(
            domain=domain,
            started_on=datetime.utcnow(),
            status=DomainCaseRuleRun.STATUS_RUNNING,
            case_type=case_type)

        if should_use_sql_backend(domain):
            for db in get_db_aliases_for_partitioned_query():
                run_case_update_rules_for_domain_and_db.delay(domain,
                                                              now,
                                                              run_record.pk,
                                                              case_type,
                                                              db=db)
        else:
            # explicitly pass db=None so that the serial task decorator has access to db in the key generation
            run_case_update_rules_for_domain_and_db.delay(domain,
                                                          now,
                                                          run_record.pk,
                                                          case_type,
                                                          db=None)
Exemplo n.º 21
0
def run_case_update_rules_for_domain(domain, now=None):
    domain_obj = Domain.get_by_name(domain)
    max_allowed_updates = domain_obj.auto_case_update_limit or settings.MAX_RULE_UPDATES_IN_ONE_RUN

    now = now or datetime.utcnow()
    start_run = datetime.utcnow()
    last_migration_check_time = None
    run_record = DomainCaseRuleRun.objects.create(
        domain=domain,
        started_on=start_run,
        status=DomainCaseRuleRun.STATUS_RUNNING,
    )
    cases_checked = 0
    case_update_result = CaseRuleActionResult()

    all_rules = AutomaticUpdateRule.by_domain(
        domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE)
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(
        all_rules)

    for case_type, rules in six.iteritems(rules_by_case_type):
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_ids = list(
            AutomaticUpdateRule.get_case_ids(domain, case_type, boundary_date))

        for case in CaseAccessors(domain).iter_cases(case_ids):
            migration_in_progress, last_migration_check_time = check_data_migration_in_progress(
                domain, last_migration_check_time)

            time_elapsed = datetime.utcnow() - start_run
            if (time_elapsed.seconds > HALT_AFTER
                    or case_update_result.total_updates >= max_allowed_updates
                    or migration_in_progress):
                run_record.done(DomainCaseRuleRun.STATUS_HALTED, cases_checked,
                                case_update_result)
                notify_error("Halting rule run for domain %s." % domain)
                return

            case_update_result.add_result(run_rules_for_case(case, rules, now))
            cases_checked += 1

        for rule in rules:
            rule.last_run = now
            rule.save()

    run_record.done(DomainCaseRuleRun.STATUS_FINISHED, cases_checked,
                    case_update_result)
Exemplo n.º 22
0
def _get_cached_rule(domain, rule_id):
    rules = AutomaticUpdateRule.by_domain_cached(domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)
    rules = list(filter(lambda rule: rule.pk == rule_id, rules))

    if len(rules) != 1:
        return None

    return rules[0]
Exemplo n.º 23
0
    def test_auto_case_update_rules(self):
        from corehq.apps.data_interfaces.models import (
            AutomaticUpdateRule, AutomaticUpdateRuleCriteria, AutomaticUpdateAction
        )
        expected_object_counts = Counter({
            AutomaticUpdateRule: 1,
            AutomaticUpdateRuleCriteria: 1,
            AutomaticUpdateAction: 2,
        })

        pre_rule = AutomaticUpdateRule(
            domain=self.domain_name,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        pre_rule.save()
        pre_criteria = AutomaticUpdateRuleCriteria.objects.create(
            property_name='last_visit_date',
            property_value='30',
            match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
            rule=pre_rule,
        )
        pre_action_update = AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='update_flag',
            property_value='Y',
            rule=pre_rule,
        )
        pre_action_close = AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=pre_rule,
        )

        self._dump_and_load(expected_object_counts)

        post_rule = AutomaticUpdateRule.objects.get(pk=pre_rule.pk)
        post_criteria = AutomaticUpdateRuleCriteria.objects.get(pk=pre_criteria.pk)
        post_action_update = AutomaticUpdateAction.objects.get(pk=pre_action_update.pk)
        post_action_close = AutomaticUpdateAction.objects.get(pk=pre_action_close.pk)

        self.assertModelsEqual(
            [pre_rule, pre_criteria, pre_action_update, pre_action_close],
            [post_rule, post_criteria, post_action_update, post_action_close]
        )
Exemplo n.º 24
0
def run_case_update_rules_for_domain(domain, now=None):
    now = now or datetime.utcnow()
    all_rules = AutomaticUpdateRule.by_domain(domain)
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(all_rules)

    for case_type, rules in rules_by_case_type.iteritems():
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_ids = AutomaticUpdateRule.get_case_ids(domain, boundary_date, case_type)

        for doc in iter_docs(CommCareCase.get_db(), case_ids):
            case = CommCareCase.wrap(doc)
            for rule in rules:
                rule.apply_rule(case, now)

        for rule in rules:
            rule.last_run = now
            rule.save()
Exemplo n.º 25
0
    def post(self, request, *args, **kwargs):
        if self.async_response is not None:
            return self.async_response

        basic_info_form_valid = self.basic_info_form.is_valid()
        criteria_form_valid = self.criteria_form.is_valid()
        schedule_form_valid = self.schedule_form.is_valid()

        if self.read_only_mode:
            # Don't allow making changes to rules that have custom
            # criteria/actions unless the user has permission to
            return HttpResponseBadRequest()

        if basic_info_form_valid and criteria_form_valid and schedule_form_valid:
            if not self.is_system_admin and (
                self.criteria_form.requires_system_admin_to_save or
                self.schedule_form.requires_system_admin_to_save
            ):
                # Don't allow adding custom criteria/actions to rules
                # unless the user has permission to
                return HttpResponseBadRequest()

            if not self.can_use_inbound_sms and self.schedule_form.uses_sms_survey:
                return HttpResponseBadRequest(
                    "Cannot create or edit survey reminders because subscription "
                    "does not have access to inbound SMS"
                )

            with transaction.atomic():
                if self.rule:
                    rule = self.rule
                else:
                    rule = AutomaticUpdateRule(
                        domain=self.domain,
                        active=True,
                        workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                    )

                rule.name = self.basic_info_form.cleaned_data['name']
                self.criteria_form.save_criteria(rule)
                self.schedule_form.save_rule_action_and_schedule(rule)

            initiate_messaging_rule_run(rule.domain, rule.pk)
            return HttpResponseRedirect(reverse(ConditionalAlertListView.urlname, args=[self.domain]))

        return self.get(request, *args, **kwargs)
Exemplo n.º 26
0
 def response_data_cleanup(domain, new_plan_version):
     """
     Any active automatic case update rules should be deactivated.
     """
     try:
         AutomaticUpdateRule.by_domain(
             domain.name,
             AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
         ).update(active=False)
         AutomaticUpdateRule.clear_caches(domain.name, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE)
         return True
     except Exception:
         log_accounting_error(
             "Failed to deactivate automatic update rules "
             "for domain %s." % domain.name
         )
         return False
Exemplo n.º 27
0
def _get_cached_rule(domain, rule_id):
    rules = AutomaticUpdateRule.by_domain_cached(domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)
    rules = [rule for rule in rules if rule.pk == rule_id]

    if len(rules) != 1:
        return None

    return rules[0]
Exemplo n.º 28
0
def domain_has_reminders(domain):
    from corehq.apps.data_interfaces.models import AutomaticUpdateRule
    from corehq.messaging.scheduling.models import ScheduledBroadcast, ImmediateBroadcast

    return (
        AutomaticUpdateRule.domain_has_conditional_alerts(domain) or
        ScheduledBroadcast.domain_has_broadcasts(domain) or
        ImmediateBroadcast.domain_has_broadcasts(domain)
    )
Exemplo n.º 29
0
def run_case_update_rules_for_domain(domain, now=None):
    now = now or datetime.utcnow()
    all_rules = AutomaticUpdateRule.by_domain(domain)
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(all_rules)

    for case_type, rules in rules_by_case_type.iteritems():
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_ids = AutomaticUpdateRule.get_case_ids(domain, case_type, boundary_date)

        for case in CaseAccessors(domain).iter_cases(case_ids):
            for rule in rules:
                stop_processing = rule.apply_rule(case, now)
                if stop_processing:
                    break

        for rule in rules:
            rule.last_run = now
            rule.save()
Exemplo n.º 30
0
    def setUp(self):
        self.now = datetime(2015, 10, 22, 0, 0)
        self.domain = "auto-update-test"
        self.rule = AutomaticUpdateRule(
            domain=self.domain, name="test-rule", case_type="test-case-type", active=True, server_modified_boundary=30
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name="last_visit_date",
                property_value="30",
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            )
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE, property_name="update_flag", property_value="Y"
            ),
            AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name="test-rule-2",
            case_type="test-case-type-2",
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE)]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name="test-rule-3",
            case_type="test-case-type-2",
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE)]

        case = CommCareCase(domain=self.domain, type="test-case-type")
        case.save()
        self.case_id = case.get_id
Exemplo n.º 31
0
def _get_active_scheduling_rules(domain, survey_only=False):
    rules = AutomaticUpdateRule.by_domain(domain.name, AutomaticUpdateRule.WORKFLOW_SCHEDULING, active_only=False)

    result = []
    for rule in rules:
        schedule = rule.get_messaging_rule_schedule()
        if schedule.active and (not survey_only or schedule.memoized_uses_sms_survey):
            result.append(rule)

    return result
Exemplo n.º 32
0
def _sync_case_for_messaging(domain, case_id):
    try:
        case = CaseAccessors(domain).get_case(case_id)
        sms_tasks.clear_case_caches(case)
    except CaseNotFound:
        case = None

    if case is None or case.is_deleted:
        sms_tasks.delete_phone_numbers_for_owners([case_id])
        delete_schedule_instances_for_cases(domain, [case_id])
        return

    if use_phone_entries():
        sms_tasks._sync_case_phone_number(case)

    rules = AutomaticUpdateRule.by_domain_cached(case.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING)
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)
    for rule in rules_by_case_type.get(case.type, []):
        rule.run_rule(case, utcnow())
Exemplo n.º 33
0
def get_auto_update_rules(domain):
    rules = AutomaticUpdateRule.by_domain(
        domain,
        # For now only grab those rules that update cases, not conditional alerts for messaging
        AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
        active_only=False)

    data = []
    for rule in rules:
        criterias = rule.caserulecriteria_set.all()
        actions = rule.caseruleaction_set.all()

        rule_data = {
            "rule":
            rule.to_json(),
            "criteria": [{
                "match_property_definition": {
                    "property_name":
                    case_rule_criter.match_property_definition.property_name,
                    "property_value":
                    case_rule_criter.match_property_definition.property_value,
                    "match_type":
                    case_rule_criter.match_property_definition.match_type
                } if case_rule_criter.match_property_definition is not None
                else None,
                "custom_match_definition": {
                    "name": case_rule_criter.custom_match_definition.name,
                } if case_rule_criter.custom_match_definition is not None else
                None,
                "closed_parent_definition":
                case_rule_criter.closed_parent_definition is not None
            } for case_rule_criter in criterias],
            "actions": [{
                "update_case_definition": {
                    "properties_to_update":
                    case_rule_action.update_case_definition.
                    properties_to_update,
                    "close_case":
                    case_rule_action.update_case_definition.close_case
                } if case_rule_action.update_case_definition is not None else
                None,
                "custom_action_definition": {
                    "name": case_rule_action.custom_action_definition.name
                } if case_rule_action.custom_action_definition is not None else
                None,
            } for case_rule_action in actions]
        }

        # Delete unnecessary data for running rules
        del rule_data['rule']['last_run']
        del rule_data['rule']['locked_for_editing']

        data.append(rule_data)

    return data
Exemplo n.º 34
0
    def test_parent_cases(self):
        with _with_case(self.domain, 'test-child-case-type', datetime(2016, 1, 1)) as child, \
                _with_case(self.domain, 'test-parent-case-type', datetime(2016, 1, 1), case_name='abc') as parent:

            # Set the parent case relationship
            child = set_parent_case(self.domain, child, parent)

            # Create a rule that references parent/name which should match
            rule = AutomaticUpdateRule(
                domain=self.domain,
                name='test-parent-rule',
                case_type='test-child-case-type',
                active=True,
                server_modified_boundary=30,
            )
            rule.save()
            self.addCleanup(rule.delete)
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='parent/name',
                property_value='abc',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=rule,
            )
            AutomaticUpdateAction.objects.create(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='parent/update_flag',
                property_value='P',
                rule=rule,
            )
            AutomaticUpdateAction.objects.create(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='parent_name',
                property_value='parent/name',
                property_value_type=AutomaticUpdateAction.CASE_PROPERTY,
                rule=rule,
            )

            # rule should match on parent case property and update parent case
            rule.apply_rule(child, datetime(2016, 3, 1))
            updated_parent = self.case_db.get_case(parent.case_id)
            updated_child = self.case_db.get_case(child.case_id)
            self.assertEqual(updated_parent.get_case_property('update_flag'),
                             'P')
            self.assertEqual(updated_child.get_case_property('parent_name'),
                             'abc')

            # Update the rule to match on a different name and now it shouldn't match
            rule.automaticupdaterulecriteria_set.all().delete()
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='parent/name',
                property_value='def',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=rule,
            )

            self.assertFalse(
                rule.rule_matches_case(child, datetime(2016, 3, 1)))
Exemplo n.º 35
0
    def _set_up_rule(self, include_closed):
        self.rule = AutomaticUpdateRule.objects.create(
            domain=self.domain,
            name='test',
            case_type=self.case_type,
            active=True,
            deleted=False,
            filter_on_server_modified=False,
            server_modified_boundary=None,
            workflow=AutomaticUpdateRule.WORKFLOW_DEDUPLICATE,
        )
        _, self.action = self.rule.add_action(
            CaseDeduplicationActionDefinition,
            match_type=CaseDeduplicationMatchTypeChoices.ALL,
            case_properties=["case_name", "age"],
            include_closed=include_closed)

        self.action.save()
        AutomaticUpdateRule.clear_caches(
            self.domain, AutomaticUpdateRule.WORKFLOW_DEDUPLICATE)
Exemplo n.º 36
0
def run_case_update_rules_on_save(case):
    key = 'case-update-on-save-case-{case}'.format(case=case.case_id)
    with CriticalSection([key]):
        update_case = True
        if case.xform_ids:
            last_form = FormAccessors(case.domain).get_form(case.xform_ids[-1])
            update_case = last_form.xmlns != AUTO_UPDATE_XMLNS
        if update_case:
            rules = AutomaticUpdateRule.by_domain(case.domain,
                AutomaticUpdateRule.WORKFLOW_CASE_UPDATE).filter(case_type=case.type)
            now = datetime.utcnow()
            run_rules_for_case(case, rules, now)
Exemplo n.º 37
0
def run_case_update_rules_for_domain_and_db(domain, now, run_id, db=None):
    domain_obj = Domain.get_by_name(domain)
    max_allowed_updates = domain_obj.auto_case_update_limit or settings.MAX_RULE_UPDATES_IN_ONE_RUN
    start_run = datetime.utcnow()

    last_migration_check_time = None
    cases_checked = 0
    case_update_result = CaseRuleActionResult()

    all_rules = list(AutomaticUpdateRule.by_domain(domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE))
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(all_rules)

    for case_type, rules in six.iteritems(rules_by_case_type):
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_ids = list(AutomaticUpdateRule.get_case_ids(domain, case_type, boundary_date, db=db))

        for case in CaseAccessors(domain).iter_cases(case_ids):
            migration_in_progress, last_migration_check_time = check_data_migration_in_progress(domain,
                last_migration_check_time)

            time_elapsed = datetime.utcnow() - start_run
            if (
                time_elapsed.seconds > HALT_AFTER or
                case_update_result.total_updates >= max_allowed_updates or
                migration_in_progress
            ):
                DomainCaseRuleRun.done(run_id, DomainCaseRuleRun.STATUS_HALTED, cases_checked, case_update_result,
                    db=db)
                notify_error("Halting rule run for domain %s." % domain)
                return

            case_update_result.add_result(run_rules_for_case(case, rules, now))
            cases_checked += 1

    run = DomainCaseRuleRun.done(run_id, DomainCaseRuleRun.STATUS_FINISHED, cases_checked, case_update_result,
        db=db)

    if run.status == DomainCaseRuleRun.STATUS_FINISHED:
        for rule in all_rules:
            AutomaticUpdateRule.objects.filter(pk=rule.pk).update(last_run=now)
Exemplo n.º 38
0
    def setUpClass(cls):
        super().setUpClass()

        cls.domain = 'naboo'
        cls.case_type = 'people'
        cls.factory = CaseFactory(cls.domain)
        cls.pillow = get_xform_pillow(skip_ucr=True)

        cls.rule = AutomaticUpdateRule.objects.create(
            domain=cls.domain,
            name='test',
            case_type=cls.case_type,
            active=True,
            deleted=False,
            filter_on_server_modified=False,
            server_modified_boundary=None,
            workflow=AutomaticUpdateRule.WORKFLOW_DEDUPLICATE,
        )
        _, cls.action = cls.rule.add_action(
            CaseDeduplicationActionDefinition,
            match_type=CaseDeduplicationMatchTypeChoices.ALL,
            case_properties=["case_name", "age"],
        )
        cls.action.set_properties_to_update([
            CaseDeduplicationActionDefinition.PropertyDefinition(
                name='age',
                value_type=CaseDeduplicationActionDefinition.VALUE_TYPE_EXACT,
                value='5',
            ),
            CaseDeduplicationActionDefinition.PropertyDefinition(
                name='case_name',
                value_type=CaseDeduplicationActionDefinition.VALUE_TYPE_EXACT,
                value='Herman Miller',
            )
        ])
        cls.action.save()
        AutomaticUpdateRule.clear_caches(
            cls.domain, AutomaticUpdateRule.WORKFLOW_DEDUPLICATE)
Exemplo n.º 39
0
def run_case_update_rules_on_save(case):
    key = 'case-update-on-save-case-{case}'.format(case=case.case_id)
    with CriticalSection([key]):
        update_case = True
        if case.xform_ids:
            last_form = FormAccessors(case.domain).get_form(case.xform_ids[-1])
            update_case = last_form.xmlns != AUTO_UPDATE_XMLNS
        if update_case:
            rules = AutomaticUpdateRule.by_domain(case.domain).filter(case_type=case.type)
            now = datetime.utcnow()
            for rule in rules:
                stop_processing = rule.apply_rule(case, now)
                if stop_processing:
                    break
Exemplo n.º 40
0
def run_case_update_rules_for_domain(domain, now=None):
    now = now or datetime.utcnow()
    start_run = datetime.utcnow()
    run_record = DomainCaseRuleRun.objects.create(
        domain=domain,
        started_on=start_run,
        status=DomainCaseRuleRun.STATUS_RUNNING,
    )
    cases_checked = 0
    case_update_result = CaseRuleActionResult()

    all_rules = AutomaticUpdateRule.by_domain(domain)
    rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(all_rules)

    for case_type, rules in rules_by_case_type.iteritems():
        boundary_date = AutomaticUpdateRule.get_boundary_date(rules, now)
        case_id_chunks = AutomaticUpdateRule.get_case_ids(domain, case_type, boundary_date)

        for case_ids in case_id_chunks:
            for case in CaseAccessors(domain).iter_cases(case_ids):
                time_elapsed = datetime.utcnow() - start_run
                if (
                    time_elapsed.seconds > HALT_AFTER or
                    case_update_result.total_updates >= settings.MAX_RULE_UPDATES_IN_ONE_RUN
                ):
                    run_record.done(DomainCaseRuleRun.STATUS_HALTED, cases_checked, case_update_result)
                    notify_error("Halting rule run for domain %s." % domain)
                    return

                case_update_result.add_result(run_rules_for_case(case, rules, now))
                cases_checked += 1

        for rule in rules:
            rule.last_run = now
            rule.save()

    run_record.done(DomainCaseRuleRun.STATUS_FINISHED, cases_checked, case_update_result)
Exemplo n.º 41
0
class AutomaticCaseUpdateTest(TestCase):
    def setUp(self):
        self.domain = 'auto-update-test'
        self.case_db = CaseAccessors(self.domain)
        self.factory = CaseFactory(self.domain)
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            ),
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='update_flag',
                property_value='Y',
            ),
            AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE, ),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [
            AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE, ),
        ]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [
            AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE, ),
        ]

        with drop_connected_signals(case_post_save):
            case = self.factory.create_case(case_type='test-case-type')
        self.case_id = case.case_id
Exemplo n.º 42
0
    def test_parent_cases(self):
        with _with_case(self.domain, 'test-child-case-type', datetime(2016, 1, 1)) as child, \
                _with_case(self.domain, 'test-parent-case-type', datetime(2016, 1, 1), case_name='abc') as parent:

            # Set the parent case relationship
            child = set_parent_case(self.domain, child, parent)

            # Create a rule that references parent/name which should match
            rule = AutomaticUpdateRule(
                domain=self.domain,
                name='test-parent-rule',
                case_type='test-child-case-type',
                active=True,
                server_modified_boundary=30,
            )
            rule.save()
            self.addCleanup(rule.delete)
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='parent/name',
                property_value='abc',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=rule,
            )
            AutomaticUpdateAction.objects.create(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='parent/update_flag',
                property_value='P',
                rule=rule,
            )
            AutomaticUpdateAction.objects.create(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='parent_name',
                property_value='parent/name',
                property_value_type=AutomaticUpdateAction.CASE_PROPERTY,
                rule=rule,
            )

            # rule should match on parent case property and update parent case
            rule.apply_rule(child, datetime(2016, 3, 1))
            updated_parent = self.case_db.get_case(parent.case_id)
            updated_child = self.case_db.get_case(child.case_id)
            self.assertEqual(updated_parent.get_case_property('update_flag'), 'P')
            self.assertEqual(updated_child.get_case_property('parent_name'), 'abc')

            # Update the rule to match on a different name and now it shouldn't match
            rule.automaticupdaterulecriteria_set.all().delete()
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='parent/name',
                property_value='def',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=rule,
            )

            self.assertFalse(rule.rule_matches_case(child, datetime(2016, 3, 1)))
Exemplo n.º 43
0
 def response_data_cleanup(domain, new_plan_version):
     """
     Any active automatic case update rules should be deactivated.
     """
     rule_count = AutomaticUpdateRule.by_domain(
         domain.name,
         AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
     ).count()
     if rule_count > 0:
         return _fmt_alert(
             ungettext(
                 "You have %(rule_count)d automatic case update rule "
                 "configured in your project. If you select this plan, "
                 "this rule will be deactivated.",
                 "You have %(rule_count)d automatic case update rules "
                 "configured in your project. If you select this plan, "
                 "these rules will be deactivated.", rule_count) % {
                     'rule_count': rule_count,
                 })
Exemplo n.º 44
0
    def test_parent_case_lookup(self):
        with _with_case(self.domain, 'test-child-case-type', datetime(2016, 1, 1)) as child, \
                _with_case(self.domain, 'test-parent-case-type', datetime(2016, 1, 1), case_name='abc') as parent:

            # Set the parent case relationship
            child = set_parent_case(self.domain, child, parent)

            # Create a rule that references parent/name which should match
            rule = AutomaticUpdateRule(
                domain=self.domain,
                name='test-parent-rule',
                case_type='test-child-case-type',
                active=True,
                server_modified_boundary=30,
            )
            rule.save()
            self.addCleanup(rule.delete)
            rule.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='parent/name',
                    property_value='abc',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                ),
            ]

            self.assertTrue(rule.rule_matches_case(child, datetime(2016, 3,
                                                                   1)))

            # Update the rule to match on a different name and now it shouldn't match
            rule.automaticupdaterulecriteria_set.all().delete()
            rule.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='parent/name',
                    property_value='def',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                ),
            ]

            self.assertFalse(
                rule.rule_matches_case(child, datetime(2016, 3, 1)))
Exemplo n.º 45
0
    def handle(self, domain, **options):
        result = []
        for rule in AutomaticUpdateRule.by_domain(
                domain,
                AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                active_only=False,
        ):
            json_rule = self.get_json_rule(rule)

            action = rule.memoized_actions[0].definition
            if action.schedule.location_type_filter:
                raise CommandError(
                    "Expected location_type_filter to be empty for rule %s. Location type filtering "
                    "references primary keys of LocationType objects which aren't guaranteed to be "
                    "the same in the imported project. This rule must be excluded from export."
                    % rule.pk)

            if isinstance(action.schedule, TimedSchedule):
                json_schedule = self.get_json_timed_schedule(action.schedule)
            elif isinstance(action.schedule, AlertSchedule):
                json_schedule = self.get_json_alert_schedule(action.schedule)
            else:
                raise CommandError(
                    "Unexpected Schedule type for rule %s. Support must be added to this script for "
                    "anything other than TimedSchedules or AlertSchedules." %
                    rule.pk)

            result.append(
                json.dumps({
                    'rule': json_rule.to_json(),
                    'schedule': json_schedule.to_json(),
                }))

        with open('conditional_alerts_for_%s.txt' % domain,
                  'w',
                  encoding='utf-8') as f:
            for line in result:
                f.write(line)
                f.write('\n')

        print("Done")
Exemplo n.º 46
0
 def response_data_cleanup(domain, new_plan_version):
     """
     Any active automatic case update rules should be deactivated.
     """
     rule_count = AutomaticUpdateRule.by_domain(
         domain.name,
         AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
     ).count()
     if rule_count > 0:
         return _fmt_alert(
             ungettext(
                 "You have %(rule_count)d automatic case update rule "
                 "configured in your project. If you select this plan, "
                 "this rule will be deactivated.",
                 "You have %(rule_count)d automatic case update rules "
                 "configured in your project. If you select this plan, "
                 "these rules will be deactivated.",
                 rule_count
             ) % {
                 'rule_count': rule_count,
             }
         )
Exemplo n.º 47
0
    def setUp(self):
        super(AutomaticCaseUpdateTest, self).setUp()
        self.domain = 'auto-update-test'
        update_toggle_cache(AUTO_CASE_UPDATE_ENHANCEMENTS.slug, self.domain, True, NAMESPACE_DOMAIN)
        update_toggle_cache(RUN_AUTO_CASE_UPDATES_ON_SAVE.slug, self.domain, True, NAMESPACE_DOMAIN)
        self.case_db = CaseAccessors(self.domain)
        self.factory = CaseFactory(self.domain)
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        AutomaticUpdateRuleCriteria.objects.create(
            property_name='last_visit_date',
            property_value='30',
            match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
            rule=self.rule,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='update_flag',
            property_value='Y',
            rule=self.rule,
        )

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=self.rule2,
        )

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=self.rule3,
        )

        self.rule4 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-4',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule4.save()
        AutomaticUpdateRuleCriteria.objects.create(
            property_name='last_visit_date',
            property_value='40',
            match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
            rule=self.rule4,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='update_flag',
            property_value='C',
            rule=self.rule4,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=self.rule4,
        )
        self.rule5 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-5',
            case_type='test-case-type-3',
            active=True,
            filter_on_server_modified=False
        )
        self.rule5.save()
        AutomaticUpdateRuleCriteria.objects.create(
            property_name='name',
            property_value='signal',
            match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
            rule=self.rule5,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='after_save',
            property_value='updated',
            rule=self.rule5,
        )

        with drop_connected_signals(case_post_save):
            case = self.factory.create_case(case_type='test-case-type')
        self.case_id = case.case_id
class AutomaticCaseUpdateTest(TestCase):
    def setUp(self):
        self.domain = 'auto-update-test'
        self.case_db = CaseAccessors(self.domain)
        self.factory = CaseFactory(self.domain)
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            ),
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='update_flag',
                property_value='Y',
            ),
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        with drop_connected_signals(case_post_save):
            case = self.factory.create_case(case_type='test-case-type')
        self.case_id = case.case_id

    def tearDown(self):
        AutomaticUpdateRuleCriteria.objects.all().delete()
        AutomaticUpdateAction.objects.all().delete()
        AutomaticUpdateRule.objects.all().delete()
        FormProcessorTestUtils.delete_all_cases(self.domain)

    def _get_case_ids(self, *args, **kwargs):
        return [self.case_id]

    def _get_case(self):
        return self.case_db.get_case(self.case_id)

    def _assert_case_revision(self, rev_number, last_modified, expect_modified=False):
        if should_use_sql_backend(self.domain):
            self.assertEqual(
                expect_modified,
                CaseAccessorSQL.case_modified_since(self.case_id, last_modified)
            )
        else:
            doc = self._get_case()
            self.assertTrue(doc['_rev'].startswith('%s-' % rev_number))

    @run_with_all_backends
    def test_rule(self):
        now = datetime(2015, 10, 22, 0, 0)
        with patch('corehq.apps.data_interfaces.models.AutomaticUpdateRule.get_case_ids', new=self._get_case_ids):
            # No update: both dates are 27 days away
            last_modified = datetime(2015, 9, 25, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 25))
            self._assert_case_revision(2, last_modified)
            run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(2, last_modified)

            # No update: server_modified_on is 32 days away but last_visit_date is 27 days away
            last_modified = datetime(2015, 9, 20, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 25))
            self._assert_case_revision(3, last_modified)
            run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(3, last_modified)

            # No update: last_visit_date is 32 days away but server_modified_on is 27 days away
            last_modified = datetime(2015, 9, 25, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 20))
            self._assert_case_revision(4, last_modified)
            run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(4, last_modified)

            # Perform update: both dates are 32 days away
            last_modified = datetime(2015, 9, 20, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 20))
            self._assert_case_revision(5, last_modified)
            with drop_connected_signals(case_post_save):
                run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(6, last_modified, True)

            case = self._get_case()
            self.assertEqual(case.get_case_property('update_flag'), 'Y')
            self.assertEqual(case.closed, True)

    @run_with_all_backends
    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)))

    @run_with_all_backends
    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)))

    @run_with_all_backends
    def test_match_not_equal(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            self.rule2.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='property2',
                    property_value='value2',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
                ),
            ]
            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)))

    @run_with_all_backends
    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:
            self.rule2.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='property1',
                    property_value='2016-02-24',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                ),
            ]

            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)))

    @run_with_all_backends
    def test_date_case_properties_for_inequality(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='2016-02-24',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
                ),
            ]

            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)))

    @run_with_all_backends
    def test_match_has_value(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            self.rule2.automaticupdaterulecriteria_set = [
                AutomaticUpdateRuleCriteria(
                    property_name='property3',
                    match_type=AutomaticUpdateRuleCriteria.MATCH_HAS_VALUE,
                ),
            ]
            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)))

    @run_with_all_backends
    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_get_rules_from_domain(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        expected_case_types = ['test-case-type', 'test-case-type-2']
        actual_case_types = rules_by_case_type.keys()
        self.assertEqual(set(expected_case_types), set(actual_case_types))

        expected_rule_ids = [self.rule.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule2.pk, self.rule3.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type-2']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

    def test_boundary_date(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type-2'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))
    def setUp(self):
        self.domain = 'auto-update-test'
        self.case_db = CaseAccessors(self.domain)
        self.factory = CaseFactory(self.domain)
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            ),
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='update_flag',
                property_value='Y',
            ),
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        with drop_connected_signals(case_post_save):
            case = self.factory.create_case(case_type='test-case-type')
        self.case_id = case.case_id
Exemplo n.º 50
0
 def _rules(self):
     return AutomaticUpdateRule.by_domain(
         self.domain,
         AutomaticUpdateRule.WORKFLOW_CASE_UPDATE,
         active_only=False,
     ).order_by('name', 'id')
Exemplo n.º 51
0
class AutomaticCaseUpdateTest(TestCase):
    def setUp(self):
        self.now = datetime(2015, 10, 22, 0, 0)
        self.domain = 'auto-update-test'
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            ),
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='update_flag',
                property_value='Y',
            ),
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        case = CommCareCase(domain=self.domain, type='test-case-type')
        case.save()
        self.case_id = case.get_id

    def tearDown(self):
        AutomaticUpdateRuleCriteria.objects.filter(rule_id=self.rule.pk).delete()
        AutomaticUpdateAction.objects.filter(rule_id=self.rule.pk).delete()
        self.rule.delete()
        CommCareCase.get(self.case_id).delete()

    def _get_case_ids(self, *args, **kwargs):
        return [self.case_id]

    def _get_case(self):
        return CommCareCase.get_db().get(self.case_id)

    def _update_case(self, server_modified_on, last_visit_date):
        doc = self._get_case()
        doc['server_modified_on'] = json_format_datetime(server_modified_on)
        doc['last_visit_date'] = last_visit_date.strftime('%Y-%m-%d')
        CommCareCase.get_db().save_doc(doc)

    def _assert_case_revision(self, number):
        doc = CommCareCase.get_db().get(self.case_id)
        self.assertTrue(doc['_rev'].startswith('%s-' % number))

    def test_rule(self):
        self._assert_case_revision(1)
        with patch('corehq.apps.data_interfaces.models.AutomaticUpdateRule.get_case_ids', new=self._get_case_ids):
            # No update: both dates are 27 days away
            self._update_case(datetime(2015, 9, 25, 12, 0), date(2015, 9, 25))
            self._assert_case_revision(2)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(2)

            # No update: server_modified_on is 32 days away but last_visit_date is 27 days away
            self._update_case(datetime(2015, 9, 20, 12, 0), date(2015, 9, 25))
            self._assert_case_revision(3)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(3)

            # No update: last_visit_date is 32 days away but server_modified_on is 27 days away
            self._update_case(datetime(2015, 9, 25, 12, 0), date(2015, 9, 20))
            self._assert_case_revision(4)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(4)

            # Perform update: both dates are 32 days away
            self._update_case(datetime(2015, 9, 20, 12, 0), date(2015, 9, 20))
            self._assert_case_revision(5)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(6)

            doc = self._get_case()
            self.assertEqual(doc['update_flag'], 'Y')
            self.assertEqual(doc['closed'], True)
Exemplo n.º 52
0
    def save_copy(self, new_domain_name=None, new_hr_name=None, user=None,
                  copy_by_id=None, share_reminders=True,
                  share_user_roles=True):
        from corehq.apps.app_manager.dbaccessors import get_app
        from corehq.apps.data_interfaces.models import AutomaticUpdateRule
        from corehq.apps.fixtures.models import FixtureDataItem
        from corehq.apps.app_manager.dbaccessors import get_brief_apps_in_domain
        from corehq.apps.domain.dbaccessors import get_doc_ids_in_domain_by_class
        from corehq.apps.fixtures.models import FixtureDataType
        from corehq.apps.users.models import UserRole

        db = Domain.get_db()
        new_id = db.copy_doc(self.get_id)['id']
        if new_domain_name is None:
            new_domain_name = new_id

        with CriticalSection(['request_domain_name_{}'.format(new_domain_name)]):
            new_domain_name = Domain.generate_name(new_domain_name)
            new_domain = Domain.get(new_id)
            new_domain.name = new_domain_name
            new_domain.hr_name = new_hr_name
            new_domain.copy_history = self.get_updated_history()
            new_domain.is_snapshot = False
            new_domain.snapshot_time = None
            new_domain.organization = None  # TODO: use current user's organization (?)

            # reset stuff
            new_domain.cda.signed = False
            new_domain.cda.date = None
            new_domain.cda.type = None
            new_domain.cda.user_id = None
            new_domain.cda.user_ip = None
            new_domain.is_test = "none"
            new_domain.internal = InternalProperties()
            new_domain.creating_user = user.username if user else None
            new_domain.date_created = datetime.utcnow()
            new_domain.use_sql_backend = True
            new_domain.granted_messaging_access = False

            for field in self._dirty_fields:
                if hasattr(new_domain, field):
                    delattr(new_domain, field)

            # Saving the domain should happen before we import any apps since
            # importing apps can update the domain object (for example, if user
            # as a case needs to be enabled)
            try:
                new_domain.save()
            except PreconditionFailed:
                # This is a hack to resolve http://manage.dimagi.com/default.asp?241492
                # Following solution in
                # https://github.com/dimagi/commcare-hq/commit/d59b1e403060ade599cc4a03db0aabc4da62b668
                time.sleep(0.5)
                new_domain.save()

            new_app_components = {}  # a mapping of component's id to its copy

            def copy_data_items(old_type_id, new_type_id):
                for item in FixtureDataItem.by_data_type(self.name, old_type_id):
                    comp = self.copy_component(
                        item.doc_type, item._id, new_domain_name, user=user)
                    comp.data_type_id = new_type_id
                    comp.save()

            def get_latest_app_id(doc_id):
                app = get_app(self.name, doc_id).get_latest_saved()
                if app:
                    return app._id, app.doc_type

            for app in get_brief_apps_in_domain(self.name):
                doc_id, doc_type = app.get_id, app.doc_type
                original_doc_id = doc_id
                if copy_by_id and doc_id not in copy_by_id:
                    continue
                if not self.is_snapshot:
                    doc_id, doc_type = get_latest_app_id(doc_id) or (doc_id, doc_type)
                component = self.copy_component(doc_type, doc_id, new_domain_name, user=user)
                if component:
                    new_app_components[original_doc_id] = component

            for doc_id in get_doc_ids_in_domain_by_class(self.name, FixtureDataType):
                if copy_by_id and doc_id not in copy_by_id:
                    continue
                component = self.copy_component(
                    'FixtureDataType', doc_id, new_domain_name, user=user)
                copy_data_items(doc_id, component._id)

            def convert_form_unique_id_function(form_unique_id):
                from corehq.apps.app_manager.models import FormBase
                form = FormBase.get_form(form_unique_id)
                form_app = form.get_app()
                m_index, f_index = form_app.get_form_location(form.unique_id)
                form_copy = new_app_components[form_app._id].get_module(m_index).get_form(f_index)
                return form_copy.unique_id

            if share_reminders:
                for rule in AutomaticUpdateRule.by_domain(
                    self.name,
                    AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                    active_only=False,
                ):
                    rule.copy_conditional_alert(
                        new_domain_name,
                        convert_form_unique_id_function=convert_form_unique_id_function,
                    )
            if share_user_roles:
                for doc_id in get_doc_ids_in_domain_by_class(self.name, UserRole):
                    self.copy_component('UserRole', doc_id, new_domain_name, user=user)

        if user:
            def add_dom_to_user(user):
                user.add_domain_membership(new_domain_name, is_admin=True)
            apply_update(user, add_dom_to_user)

        return new_domain
Exemplo n.º 53
0
class AutomaticCaseUpdateTest(TestCase):
    def setUp(self):
        self.now = datetime(2015, 10, 22, 0, 0)
        self.domain = 'auto-update-test'
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='last_visit_date',
                property_value='30',
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            ),
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='update_flag',
                property_value='Y',
            ),
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_CLOSE,
            ),
        ]

        case = CommCareCase(domain=self.domain, type='test-case-type')
        case.save()
        self.case_id = case.get_id

    def tearDown(self):
        AutomaticUpdateRuleCriteria.objects.all().delete()
        AutomaticUpdateAction.objects.all().delete()
        AutomaticUpdateRule.objects.all().delete()
        CommCareCase.get(self.case_id).delete()

    def _get_case_ids(self, *args, **kwargs):
        return [self.case_id]

    def _get_case(self):
        return CommCareCase.get_db().get(self.case_id)

    def _update_case(self, server_modified_on, last_visit_date):
        doc = self._get_case()
        doc['server_modified_on'] = json_format_datetime(server_modified_on)
        doc['last_visit_date'] = last_visit_date.strftime('%Y-%m-%d')
        CommCareCase.get_db().save_doc(doc)

    def _assert_case_revision(self, number):
        doc = CommCareCase.get_db().get(self.case_id)
        self.assertTrue(doc['_rev'].startswith('%s-' % number))

    def test_rule(self):
        self._assert_case_revision(1)
        with patch('corehq.apps.data_interfaces.models.AutomaticUpdateRule.get_case_ids', new=self._get_case_ids):
            # No update: both dates are 27 days away
            self._update_case(datetime(2015, 9, 25, 12, 0), date(2015, 9, 25))
            self._assert_case_revision(2)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(2)

            # No update: server_modified_on is 32 days away but last_visit_date is 27 days away
            self._update_case(datetime(2015, 9, 20, 12, 0), date(2015, 9, 25))
            self._assert_case_revision(3)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(3)

            # No update: last_visit_date is 32 days away but server_modified_on is 27 days away
            self._update_case(datetime(2015, 9, 25, 12, 0), date(2015, 9, 20))
            self._assert_case_revision(4)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(4)

            # Perform update: both dates are 32 days away
            self._update_case(datetime(2015, 9, 20, 12, 0), date(2015, 9, 20))
            self._assert_case_revision(5)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(6)

            doc = self._get_case()
            self.assertEqual(doc['update_flag'], 'Y')
            self.assertEqual(doc['closed'], True)

    def test_match_days_since(self):
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
            server_modified_on=datetime(2015, 1, 1),
        )

        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)))

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

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

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

        case.set_case_property('last_visit_date', '2015-11-01')
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_match_equal(self):
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
            server_modified_on=datetime(2015, 1, 1),
        )

        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)))

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

        case.set_case_property('property1', 'value1')
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_match_not_equal(self):
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
            server_modified_on=datetime(2015, 1, 1),
        )

        self.rule2.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='property2',
                property_value='value2',
                match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
            ),
        ]
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

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

        case.set_case_property('property2', 'x')
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    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.
        """
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
        )

        self.rule2.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='property1',
                property_value='2016-02-24',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
            ),
        ]

        case.set_case_property('property1', '2016-02-24')
        case.save()
        case = CommCareCase.get(case.get_id)
        case.server_modified_on = datetime(2015, 1, 1)
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property('property1', '2016-02-25')
        case.save()
        case = CommCareCase.get(case.get_id)
        case.server_modified_on = datetime(2015, 1, 1)
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.delete()

    def test_date_case_properties_for_inequality(self):
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
        )

        self.rule2.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='property1',
                property_value='2016-02-24',
                match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
            ),
        ]

        case.set_case_property('property1', '2016-02-24')
        case.save()
        case = CommCareCase.get(case.get_id)
        case.server_modified_on = datetime(2015, 1, 1)
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property('property1', '2016-02-25')
        case.save()
        case = CommCareCase.get(case.get_id)
        case.server_modified_on = datetime(2015, 1, 1)
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.delete()

    def test_match_has_value(self):
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
            server_modified_on=datetime(2015, 1, 1),
        )

        self.rule2.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name='property3',
                match_type=AutomaticUpdateRuleCriteria.MATCH_HAS_VALUE,
            ),
        ]
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

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

        case.set_case_property('property3', '')
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_and_criteria(self):
        case = CommCareCase(
            domain=self.domain,
            type='test-case-type-2',
            server_modified_on=datetime(2015, 1, 1),
        )

        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,
            ),
        ]

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

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

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

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

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

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

    def test_get_rules_from_domain(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        expected_case_types = ['test-case-type', 'test-case-type-2']
        actual_case_types = rules_by_case_type.keys()
        self.assertEqual(set(expected_case_types), set(actual_case_types))

        expected_rule_ids = [self.rule.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule2.pk, self.rule3.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type-2']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

    def test_boundary_date(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type-2'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))
Exemplo n.º 54
0
class AutomaticCaseUpdateTest(TestCase):
    def setUp(self):
        self.now = datetime(2015, 10, 22, 0, 0)
        self.domain = "auto-update-test"
        self.rule = AutomaticUpdateRule(
            domain=self.domain, name="test-rule", case_type="test-case-type", active=True, server_modified_boundary=30
        )
        self.rule.save()
        self.rule.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name="last_visit_date",
                property_value="30",
                match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_SINCE,
            )
        ]
        self.rule.automaticupdateaction_set = [
            AutomaticUpdateAction(
                action=AutomaticUpdateAction.ACTION_UPDATE, property_name="update_flag", property_value="Y"
            ),
            AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE),
        ]

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name="test-rule-2",
            case_type="test-case-type-2",
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        self.rule2.automaticupdateaction_set = [AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE)]

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name="test-rule-3",
            case_type="test-case-type-2",
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        self.rule3.automaticupdateaction_set = [AutomaticUpdateAction(action=AutomaticUpdateAction.ACTION_CLOSE)]

        case = CommCareCase(domain=self.domain, type="test-case-type")
        case.save()
        self.case_id = case.get_id

    def tearDown(self):
        AutomaticUpdateRuleCriteria.objects.all().delete()
        AutomaticUpdateAction.objects.all().delete()
        AutomaticUpdateRule.objects.all().delete()
        CommCareCase.get(self.case_id).delete()

    def _get_case_ids(self, *args, **kwargs):
        return [self.case_id]

    def _get_case(self):
        return CommCareCase.get_db().get(self.case_id)

    def _update_case(self, server_modified_on, last_visit_date):
        doc = self._get_case()
        doc["server_modified_on"] = json_format_datetime(server_modified_on)
        doc["last_visit_date"] = last_visit_date.strftime("%Y-%m-%d")
        CommCareCase.get_db().save_doc(doc)

    def _assert_case_revision(self, number):
        doc = CommCareCase.get_db().get(self.case_id)
        self.assertTrue(doc["_rev"].startswith("%s-" % number))

    def test_rule(self):
        self._assert_case_revision(1)
        with patch("corehq.apps.data_interfaces.models.AutomaticUpdateRule.get_case_ids", new=self._get_case_ids):
            # No update: both dates are 27 days away
            self._update_case(datetime(2015, 9, 25, 12, 0), date(2015, 9, 25))
            self._assert_case_revision(2)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(2)

            # No update: server_modified_on is 32 days away but last_visit_date is 27 days away
            self._update_case(datetime(2015, 9, 20, 12, 0), date(2015, 9, 25))
            self._assert_case_revision(3)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(3)

            # No update: last_visit_date is 32 days away but server_modified_on is 27 days away
            self._update_case(datetime(2015, 9, 25, 12, 0), date(2015, 9, 20))
            self._assert_case_revision(4)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(4)

            # Perform update: both dates are 32 days away
            self._update_case(datetime(2015, 9, 20, 12, 0), date(2015, 9, 20))
            self._assert_case_revision(5)
            run_case_update_rules_for_domain(self.domain, now=self.now)
            self._assert_case_revision(6)

            doc = self._get_case()
            self.assertEqual(doc["update_flag"], "Y")
            self.assertEqual(doc["closed"], True)

    def test_match_days_since(self):
        case = CommCareCase(domain=self.domain, type="test-case-type-2", server_modified_on=datetime(2015, 1, 1))

        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)))

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

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

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

        case.set_case_property("last_visit_date", "2015-11-01")
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_match_equal(self):
        case = CommCareCase(domain=self.domain, type="test-case-type-2", server_modified_on=datetime(2015, 1, 1))

        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)))

        case.set_case_property("property1", "x")
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property("property1", "value1")
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_match_not_equal(self):
        case = CommCareCase(domain=self.domain, type="test-case-type-2", server_modified_on=datetime(2015, 1, 1))

        self.rule2.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name="property2",
                property_value="value2",
                match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
            )
        ]
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property("property2", "value2")
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property("property2", "x")
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_match_has_value(self):
        case = CommCareCase(domain=self.domain, type="test-case-type-2", server_modified_on=datetime(2015, 1, 1))

        self.rule2.automaticupdaterulecriteria_set = [
            AutomaticUpdateRuleCriteria(
                property_name="property3", match_type=AutomaticUpdateRuleCriteria.MATCH_HAS_VALUE
            )
        ]
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property("property3", "x")
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property("property3", "")
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_and_criteria(self):
        case = CommCareCase(domain=self.domain, type="test-case-type-2", server_modified_on=datetime(2015, 1, 1))

        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
            ),
        ]

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

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

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

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

        case.set_case_property("property2", "x")
        case.set_case_property("property3", "")
        self.assertFalse(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

        case.set_case_property("property3", "x")
        self.assertTrue(self.rule2.rule_matches_case(case, datetime(2016, 1, 1)))

    def test_get_rules_from_domain(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        expected_case_types = ["test-case-type", "test-case-type-2"]
        actual_case_types = rules_by_case_type.keys()
        self.assertEqual(set(expected_case_types), set(actual_case_types))

        expected_rule_ids = [self.rule.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type["test-case-type"]]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule2.pk, self.rule3.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type["test-case-type-2"]]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

    def test_boundary_date(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type["test-case-type"], datetime(2016, 1, 1)
        )
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type["test-case-type-2"], datetime(2016, 1, 1)
        )
        self.assertEqual(boundary_date, datetime(2015, 12, 2))
Exemplo n.º 55
0
class AutomaticCaseUpdateTest(TestCase):

    def setUp(self):
        super(AutomaticCaseUpdateTest, self).setUp()
        self.domain = 'auto-update-test'
        update_toggle_cache(AUTO_CASE_UPDATE_ENHANCEMENTS.slug, self.domain, True, NAMESPACE_DOMAIN)
        update_toggle_cache(RUN_AUTO_CASE_UPDATES_ON_SAVE.slug, self.domain, True, NAMESPACE_DOMAIN)
        self.case_db = CaseAccessors(self.domain)
        self.factory = CaseFactory(self.domain)
        self.rule = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule.save()
        AutomaticUpdateRuleCriteria.objects.create(
            property_name='last_visit_date',
            property_value='30',
            match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
            rule=self.rule,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='update_flag',
            property_value='Y',
            rule=self.rule,
        )

        self.rule2 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-2',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=30,
        )
        self.rule2.save()
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=self.rule2,
        )

        self.rule3 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-3',
            case_type='test-case-type-2',
            active=True,
            server_modified_boundary=50,
        )
        self.rule3.save()
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=self.rule3,
        )

        self.rule4 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-4',
            case_type='test-case-type',
            active=True,
            server_modified_boundary=30,
        )
        self.rule4.save()
        AutomaticUpdateRuleCriteria.objects.create(
            property_name='last_visit_date',
            property_value='40',
            match_type=AutomaticUpdateRuleCriteria.MATCH_DAYS_AFTER,
            rule=self.rule4,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='update_flag',
            property_value='C',
            rule=self.rule4,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_CLOSE,
            rule=self.rule4,
        )
        self.rule5 = AutomaticUpdateRule(
            domain=self.domain,
            name='test-rule-5',
            case_type='test-case-type-3',
            active=True,
            filter_on_server_modified=False
        )
        self.rule5.save()
        AutomaticUpdateRuleCriteria.objects.create(
            property_name='name',
            property_value='signal',
            match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
            rule=self.rule5,
        )
        AutomaticUpdateAction.objects.create(
            action=AutomaticUpdateAction.ACTION_UPDATE,
            property_name='after_save',
            property_value='updated',
            rule=self.rule5,
        )

        with drop_connected_signals(case_post_save):
            case = self.factory.create_case(case_type='test-case-type')
        self.case_id = case.case_id

    def tearDown(self):
        AutomaticUpdateRuleCriteria.objects.all().delete()
        AutomaticUpdateAction.objects.all().delete()
        AutomaticUpdateRule.objects.all().delete()
        FormProcessorTestUtils.delete_all_cases(self.domain)
        super(AutomaticCaseUpdateTest, self).tearDown()

    def _get_case_ids(self, *args, **kwargs):
        return [self.case_id]

    def _get_case(self):
        return self.case_db.get_case(self.case_id)

    def _assert_case_revision(self, rev_number, last_modified, expect_modified=False):
        if should_use_sql_backend(self.domain):
            self.assertEqual(
                expect_modified,
                CaseAccessorSQL.case_modified_since(self.case_id, last_modified)
            )
        else:
            doc = self._get_case()
            self.assertTrue(doc['_rev'].startswith('%s-' % rev_number))

    @run_with_all_backends
    def test_rule(self):
        now = datetime(2015, 10, 22, 0, 0)
        with patch('corehq.apps.data_interfaces.models.AutomaticUpdateRule.get_case_ids', new=self._get_case_ids):
            # No update: both dates are 27 days away
            last_modified = datetime(2015, 9, 25, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 25))
            self._assert_case_revision(2, last_modified)
            run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(2, last_modified)

            # No update: server_modified_on is 32 days away but last_visit_date is 27 days away
            last_modified = datetime(2015, 9, 20, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 25))
            self._assert_case_revision(3, last_modified)
            run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(3, last_modified)

            # No update: last_visit_date is 32 days away but server_modified_on is 27 days away
            last_modified = datetime(2015, 9, 25, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 20))
            self._assert_case_revision(4, last_modified)
            run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(4, last_modified)

            # Perform update: both dates are 32 days away
            last_modified = datetime(2015, 9, 20, 12, 0)
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 20))
            self._assert_case_revision(5, last_modified)
            with drop_connected_signals(case_post_save):
                run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(6, last_modified, True)

            case = self._get_case()
            self.assertEqual(case.get_case_property('update_flag'), 'Y')

            # No update: case state matches final state
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 20))
            self._assert_case_revision(7, last_modified)
            with drop_connected_signals(case_post_save):
                run_case_update_rules_for_domain(self.domain, now=now)
            self._assert_case_revision(7, last_modified)

            # Perform update: case closed because date is 42 days away
            _update_case(self.domain, self.case_id, last_modified, date(2015, 9, 10))
            with drop_connected_signals(case_post_save):
                run_case_update_rules_for_domain(self.domain, now=now)

            case = self._get_case()
            self.assertEqual(case.get_case_property('update_flag'), 'C')
            self.assertEqual(case.closed, True)

    @run_with_all_backends
    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)))

    @run_with_all_backends
    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)))

    @run_with_all_backends
    def test_match_equal(self):
        with _with_case(self.domain, 'test-case-type-2', datetime(2015, 1, 1)) as case:
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property1',
                property_value='value1',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=self.rule2,
            )
            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)))

    @run_with_all_backends
    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)))

    @run_with_all_backends
    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)))

    @run_with_all_backends
    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)))

    @run_with_all_backends
    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)))

    @run_with_all_backends
    def test_and_criteria(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,
            )
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property1',
                property_value='value1',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=self.rule2,
            )
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property2',
                property_value='value2',
                match_type=AutomaticUpdateRuleCriteria.MATCH_NOT_EQUAL,
                rule=self.rule2,
            )
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='property3',
                match_type=AutomaticUpdateRuleCriteria.MATCH_HAS_VALUE,
                rule=self.rule2,
            )

            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_get_rules_from_domain(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        expected_case_types = ['test-case-type', 'test-case-type-2', 'test-case-type-3']
        actual_case_types = rules_by_case_type.keys()
        self.assertEqual(set(expected_case_types), set(actual_case_types))

        expected_rule_ids = [self.rule.pk, self.rule4.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule2.pk, self.rule3.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type-2']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

        expected_rule_ids = [self.rule5.pk]
        actual_rule_ids = [rule.pk for rule in rules_by_case_type['test-case-type-3']]
        self.assertEqual(set(expected_rule_ids), set(actual_rule_ids))

    def test_boundary_date(self):
        rules = AutomaticUpdateRule.by_domain(self.domain)
        rules_by_case_type = AutomaticUpdateRule.organize_rules_by_case_type(rules)

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

        boundary_date = AutomaticUpdateRule.get_boundary_date(
            rules_by_case_type['test-case-type-2'], datetime(2016, 1, 1))
        self.assertEqual(boundary_date, datetime(2015, 12, 2))

    @run_with_all_backends
    def test_parent_cases(self):
        with _with_case(self.domain, 'test-child-case-type', datetime(2016, 1, 1)) as child, \
                _with_case(self.domain, 'test-parent-case-type', datetime(2016, 1, 1), case_name='abc') as parent:

            # Set the parent case relationship
            child = set_parent_case(self.domain, child, parent)

            # Create a rule that references parent/name which should match
            rule = AutomaticUpdateRule(
                domain=self.domain,
                name='test-parent-rule',
                case_type='test-child-case-type',
                active=True,
                server_modified_boundary=30,
            )
            rule.save()
            self.addCleanup(rule.delete)
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='parent/name',
                property_value='abc',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=rule,
            )
            AutomaticUpdateAction.objects.create(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='parent/update_flag',
                property_value='P',
                rule=rule,
            )
            AutomaticUpdateAction.objects.create(
                action=AutomaticUpdateAction.ACTION_UPDATE,
                property_name='parent_name',
                property_value='parent/name',
                property_value_type=AutomaticUpdateAction.CASE_PROPERTY,
                rule=rule,
            )

            # rule should match on parent case property and update parent case
            rule.apply_rule(child, datetime(2016, 3, 1))
            updated_parent = self.case_db.get_case(parent.case_id)
            updated_child = self.case_db.get_case(child.case_id)
            self.assertEqual(updated_parent.get_case_property('update_flag'), 'P')
            self.assertEqual(updated_child.get_case_property('parent_name'), 'abc')

            # Update the rule to match on a different name and now it shouldn't match
            rule.automaticupdaterulecriteria_set.all().delete()
            AutomaticUpdateRuleCriteria.objects.create(
                property_name='parent/name',
                property_value='def',
                match_type=AutomaticUpdateRuleCriteria.MATCH_EQUAL,
                rule=rule,
            )

            self.assertFalse(rule.rule_matches_case(child, datetime(2016, 3, 1)))

    @run_with_all_backends
    def test_no_server_boundary(self):
        with _with_case(self.domain, 'test-case-type-3', datetime(2016, 1, 1), case_name='signal') as case:
            # no filtering on server modified date so same day matches
            self.assertTrue(self.rule5.rule_matches_case(case, datetime(2016, 1, 1)))

    @run_with_all_backends
    def test_run_on_save(self):
        with _with_case(self.domain, 'test-case-type-3', datetime(2016, 1, 1), case_name='signal') as case:
            with patch('corehq.apps.data_interfaces.models.AutomaticUpdateRule.apply_rule') as apply:
                # property is updated after save signal (case update used to force save)
                update_case(self.domain, case.case_id, {})
                apply.assert_called_once()

    @run_with_all_backends
    def test_early_task_exit(self):
        with _with_case(self.domain, 'test-case-type-3', datetime(2016, 1, 1), case_name='signal') as case:
            with patch('corehq.apps.data_interfaces.models.AutomaticUpdateRule.apply_rule') as apply:
                hqcase.utils.update_case(case.domain, case.case_id, case_properties={}, xmlns=AUTO_UPDATE_XMLNS)
                apply.assert_not_called()