def create_aww_indicator_3(domain): with transaction.atomic(): schedule = TimedSchedule.create_simple_daily_schedule( domain, TimedEvent(time=time(9, 0)), CustomContent(custom_content_id='ICDS_MISSED_CF_VISIT_TO_AWW'), total_iterations=1, start_offset=1, ) schedule.default_language_code = 'hin' schedule.custom_metadata = {'icds_indicator': 'aww_3'} schedule.save() rule = AutomaticUpdateRule.objects.create( domain=domain, name="AWW #3: Missed CF #1", case_type='ccs_record', active=True, deleted=False, filter_on_server_modified=False, server_modified_boundary=None, migrated=True, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, ) rule.add_criteria( MatchPropertyDefinition, property_name='cf1_date', match_type=MatchPropertyDefinition.MATCH_HAS_NO_VALUE, ) rule.add_action( CreateScheduleInstanceActionDefinition, timed_schedule_id=schedule.schedule_id, recipients=(('Owner', None),), scheduler_module_info=CreateScheduleInstanceActionDefinition.SchedulerModuleInfo( enabled=True, app_id='48cc1709b7f62ffea24cc6634a004745', form_unique_id='84ea09b6aa5aba125ec82bf2bb8dfa44cc5ea150', visit_number=0, window_position=VISIT_WINDOW_END, ), )
def _create_ls_indicator_5(domain, visit_num): with transaction.atomic(): schedule = TimedSchedule.create_simple_daily_schedule( domain, TimedEvent(time=time(9, 0)), CustomContent(custom_content_id='ICDS_MISSED_PNC_VISIT_TO_LS'), total_iterations=1, start_offset=1, ) schedule.default_language_code = 'hin' schedule.custom_metadata = {'icds_indicator': 'ls_5'} schedule.save() rule = AutomaticUpdateRule.objects.create( domain=domain, name="LS #5: Missed PNC #%s" % visit_num, case_type='ccs_record', active=True, deleted=False, filter_on_server_modified=False, server_modified_boundary=None, migrated=True, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, ) rule.add_criteria( MatchPropertyDefinition, property_name='pnc%s_date' % visit_num, match_type=MatchPropertyDefinition.MATCH_HAS_NO_VALUE, ) rule.add_action( CreateScheduleInstanceActionDefinition, timed_schedule_id=schedule.schedule_id, recipients=(('CustomRecipient', 'ICDS_SUPERVISOR_FROM_AWC_OWNER'),), scheduler_module_info=CreateScheduleInstanceActionDefinition.SchedulerModuleInfo( enabled=True, app_id='48cc1709b7f62ffea24cc6634a004745', form_unique_id='f55da33c32fb41489d5082b7b3acfe43c739e988', visit_number=visit_num - 1, window_position=VISIT_WINDOW_END, ), )
def get_json_rule(self, rule): json_rule = SimpleSchedulingRule( name=rule.name, case_type=rule.case_type, ) for criterion in rule.memoized_criteria: definition = criterion.definition if not isinstance(definition, MatchPropertyDefinition): raise CommandError( "Rule %s references currently unsupported criterion definition for export. " "Either add support to this script for unsupported criteria or exclude rule " "from export." % rule.pk ) json_rule.criteria.append(MatchPropertyCriterion( property_name=definition.property_name, property_value=definition.property_value, match_type=definition.match_type, )) if len(rule.memoized_actions) != 1: raise CommandError( "Expected exactly one action for rule %s. This is an unexpected configuration. " "Was this rule created with the UI?" % rule.pk ) action = rule.memoized_actions[0].definition if not isinstance(action, CreateScheduleInstanceActionDefinition): raise CommandError( "Expected CreateScheduleInstanceActionDefinition for rule %s. This is an unexpected " "configuration. Was this rule created with the UI?" % rule.pk ) for recipient_type, recipient_id in action.recipients: if recipient_type not in ( CaseScheduleInstanceMixin.RECIPIENT_TYPE_SELF, CaseScheduleInstanceMixin.RECIPIENT_TYPE_CASE_OWNER, CaseScheduleInstanceMixin.RECIPIENT_TYPE_LAST_SUBMITTING_USER, CaseScheduleInstanceMixin.RECIPIENT_TYPE_PARENT_CASE, CaseScheduleInstanceMixin.RECIPIENT_TYPE_CUSTOM, ): raise CommandError( "Unsupported recipient_type %s referenced in rule %s. That's probably because the " "recipient type references a specific object like a user or location whose id cannot " "be guaranteed to be the same in the imported project. This rule must be excluded " "from export" % (recipient_type, rule.pk) ) if action.get_scheduler_module_info().enabled: raise CommandError( "Scheduler module integration is not supported for export because it references " "a form whose unique id is not guaranteed to be the same in the imported project. Please " "exclude rule %s from export." % rule.pk ) json_rule.recipients = copy.deepcopy(action.recipients) json_rule.reset_case_property_name = action.reset_case_property_name json_rule.start_date_case_property = action.start_date_case_property json_rule.specific_start_date = action.specific_start_date json_rule.scheduler_module_info = CreateScheduleInstanceActionDefinition.SchedulerModuleInfo(enabled=False) return json_rule
def test_visit_scheduler_integration(self, utcnow_patch, module_and_form_patch): schedule = TimedSchedule.create_simple_daily_schedule( self.domain, TimedEvent(time=time(9, 0)), SMSContent(message={'en': 'Hello'}), total_iterations=1, ) rule = create_empty_rule(self.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING) _, definition = rule.add_action( CreateScheduleInstanceActionDefinition, timed_schedule_id=schedule.schedule_id, recipients=(('CommCareUser', self.user.get_id),) ) module, form = get_visit_scheduler_module_and_form_for_test() definition.set_scheduler_module_info(CreateScheduleInstanceActionDefinition.SchedulerModuleInfo( enabled=True, app_id='n/a for test', form_unique_id=form.unique_id, visit_number=1, window_position=VISIT_WINDOW_START, )) definition.save() AutomaticUpdateRule.clear_caches(self.domain, AutomaticUpdateRule.WORKFLOW_SCHEDULING) utcnow_patch.return_value = datetime(2017, 8, 1, 7, 0) module_and_form_patch.return_value = module, form with create_case(self.domain, 'person') as case: # Schedule phase does not match, nothing is scheduled instances = get_case_timed_schedule_instances_for_schedule(case.case_id, schedule) self.assertEqual(instances.count(), 0) update_case(self.domain, case.case_id, case_properties={'add': '2017-08-01', 'current_schedule_phase': '2'}) 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, 8, 6)) 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, 8, 6, 13, 0)) self.assertTrue(instances[0].active) # If the anchor date gets updated (due to correction, for example), the schedule recalculates update_case(self.domain, case.case_id, case_properties={'add': '2017-08-10'}) 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, 8, 15)) 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, 8, 15, 13, 0)) self.assertTrue(instances[0].active) # If the anchor date is in the past, the schedule instance is deactivated update_case(self.domain, case.case_id, case_properties={'add': '2017-07-01'}) 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, 7, 6)) 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, 2) self.assertEqual(instances[0].next_event_due, datetime(2017, 7, 7, 13, 0)) self.assertFalse(instances[0].active) # If the anchor date is reset, the schedule instance is reactivated update_case(self.domain, case.case_id, case_properties={'add': '2017-08-01'}) 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, 8, 6)) 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, 8, 6, 13, 0)) self.assertTrue(instances[0].active) # Making an arbitrary update doesn't cause any recalculating to happen with patch('corehq.messaging.scheduling.scheduling_partitioned.models.AbstractTimedScheduleInstance.recalculate_schedule') as recalculate_patch: update_case(self.domain, case.case_id, case_properties={'new_property': 'new value'}) self.assertEqual(recalculate_patch.call_count, 0) 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, 8, 6)) 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, 8, 6, 13, 0)) self.assertTrue(instances[0].active) # Terminate the schedule, no more schedule instances should be scheduled update_case(self.domain, case.case_id, case_properties={'current_schedule_phase': '-1'}) instances = get_case_timed_schedule_instances_for_schedule(case.case_id, schedule) self.assertEqual(instances.count(), 0)
class VisitSchedulerIntegrationHelperTestCase(TestCase): domain = 'visit-scheduler-integration-helper' @classmethod def setUpClass(cls): cls.module, cls.form = get_visit_scheduler_module_and_form_for_test() super(VisitSchedulerIntegrationHelperTestCase, cls).setUpClass() def get_helper(self, case, visit_number=2, window_position=VISIT_WINDOW_START): return VisitSchedulerIntegrationHelper( case, CreateScheduleInstanceActionDefinition.SchedulerModuleInfo( enabled=True, app_id='n/a for test', form_unique_id=self.form.unique_id, visit_number=visit_number, window_position=window_position, ) ) def test_get_visit_scheduler_form_phase(self): with create_case(self.domain, 'person') as case: phase_num, phase = self.get_helper(case).get_visit_scheduler_form_phase(self.module) self.assertEqual(phase_num, 2) self.assertEqual(phase.to_json(), self.module.schedule_phases[1].to_json()) def test_calculate_window_date(self): with create_case(self.domain, 'person') as case: helper = self.get_helper(case, window_position=VISIT_WINDOW_START) self.assertEqual(