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 )
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()
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
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()
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)
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)
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 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 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))
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)
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)
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])
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 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, }
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
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))
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)
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)
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]
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] )
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()
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)
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
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]
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) )
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()
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 _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
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())
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
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)))
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)
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)
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)
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)
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
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)
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 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)))
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, })
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)))
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")
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, } )
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
def _rules(self): return AutomaticUpdateRule.by_domain( self.domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE, active_only=False, ).order_by('name', 'id')
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)
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
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))
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))
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()