Esempio n. 1
0
    def get_copy_ajax_response(self, rule, copy_to_project_name):
        if not self.allow_copy:
            return JsonResponse({
                'status': 'error',
                'error_msg': _("You do not have permission to copy alerts."),
            })

        destination_project = Domain.get_by_name(copy_to_project_name)
        if (
            destination_project is None or
            destination_project.is_snapshot or
            not self.request.couch_user.has_permission(copy_to_project_name, 'edit_data')
        ):
            return JsonResponse({
                'status': 'error',
                'error_msg': _("Destination project not found."),
            })

        # Use the same copy method as the exchange uses, which will
        # return None if the rule can't be copied, otherwise will
        # copy the rule as inactive.
        copied_rule = rule.copy_conditional_alert(copy_to_project_name, allow_custom_references=True)
        if copied_rule is None:
            return JsonResponse({
                'status': 'error',
                'error_msg': _("This rule includes references that cannot be copied."),
            })

        initiate_messaging_rule_run(copied_rule.domain, copied_rule.pk)
        return JsonResponse({
            'status': 'success',
            'rule': self._format_rule_for_json(rule),
        })
Esempio n. 2
0
    def get_activate_ajax_response(self, active_flag, rule):
        """
        When we deactivate a conditional alert from the UI, we are only
        deactivating the schedule that sends the content. The rule itself
        stays active.
        This is because we want to be keeping all the schedule instances
        up to date (though inactive), so that if the schedule is reactivated,
        we don't send a large quantity of stale messages.
        """
        with transaction.atomic():
            schedule = rule.get_messaging_rule_schedule()
            if active_flag and not self.can_use_inbound_sms and schedule.memoized_uses_sms_survey:
                return HttpResponseBadRequest(
                    "Cannot create or edit survey reminders because subscription "
                    "does not have access to inbound SMS")

            if active_flag and (rule.references_parent_case
                                or schedule.references_parent_case):
                return HttpResponseBadRequest(
                    "Cannot reactivate alerts that reference parent case properties"
                )

            schedule.active = active_flag
            schedule.save()
            initiate_messaging_rule_run(self.domain, rule.pk)

        return JsonResponse({'status': 'success'})
Esempio n. 3
0
    def get_copy_ajax_response(self, rule, copy_to_project_name):
        if not self.allow_copy:
            return JsonResponse({
                'status':
                'error',
                'error_msg':
                _("You do not have permission to copy alerts."),
            })

        destination_project = Domain.get_by_name(copy_to_project_name)
        if (destination_project is None or destination_project.is_snapshot
                or not self.request.couch_user.has_permission(
                    copy_to_project_name, 'edit_data')):
            return JsonResponse({
                'status':
                'error',
                'error_msg':
                _("Destination project not found."),
            })

        # Use the same copy method as the exchange uses, which will
        # return None if the rule can't be copied, otherwise will
        # copy the rule as inactive.
        copied_rule = rule.copy_conditional_alert(copy_to_project_name,
                                                  allow_custom_references=True)
        if copied_rule is None:
            return JsonResponse({
                'status':
                'error',
                'error_msg':
                _("This rule includes references that cannot be copied."),
            })

        initiate_messaging_rule_run(copied_rule.domain, copied_rule.pk)
        return JsonResponse({'status': 'success'})
Esempio n. 4
0
    def get_restart_ajax_response(self, rule):
        helper = MessagingRuleProgressHelper(rule.pk)
        if self.limit_rule_restarts and helper.rule_initiation_key_is_set():
            minutes_remaining = helper.rule_initiation_key_minutes_remaining()
            return JsonResponse({'status': 'error', 'minutes_remaining': minutes_remaining})

        initiate_messaging_rule_run(rule.domain, rule.pk)
        return JsonResponse({'status': 'success'})
Esempio n. 5
0
    def get_restart_ajax_response(self, rule):
        helper = MessagingRuleProgressHelper(rule.pk)
        if self.limit_rule_restarts and helper.rule_initiation_key_is_set():
            minutes_remaining = helper.rule_initiation_key_minutes_remaining()
            return JsonResponse({'status': 'error', 'minutes_remaining': minutes_remaining})

        initiate_messaging_rule_run(rule.domain, rule.pk)
        return JsonResponse({
            'status': 'success',
            'rule': self._format_rule_for_json(rule),
        })
Esempio n. 6
0
    def post(self, request, *args, **kwargs):
        if self.async_response is not None:
            return self.async_response

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

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

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

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

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

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

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

        return self.get(request, *args, **kwargs)
Esempio n. 7
0
    def get_activate_ajax_response(self, active_flag, rule):
        """
        When we deactivate a conditional alert from the UI, we are only
        deactivating the schedule that sends the content. The rule itself
        stays active.
        This is because we want to be keeping all the schedule instances
        up to date (though inactive), so that if the schedule is reactivated,
        we don't send a large quantity of stale messages.
        """
        with transaction.atomic():
            schedule = rule.get_messaging_rule_schedule()
            schedule.active = active_flag
            schedule.save()
            initiate_messaging_rule_run(self.domain, rule.pk)

        return HttpResponse()
Esempio n. 8
0
    def post(self, request, *args, **kwargs):
        if self.async_response is not None:
            return self.async_response

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

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

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

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

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

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

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

        return self.get(request, *args, **kwargs)
Esempio n. 9
0
def _deactivate_schedules(domain, survey_only=False):
    """
    The subscription changes are executed within a transaction, so
    we need to make sure any celery tasks only get started after the
    transaction commits.
    """
    from corehq.messaging.tasks import initiate_messaging_rule_run

    for broadcast in _get_active_immediate_broadcasts(domain,
                                                      survey_only=survey_only):
        AlertSchedule.objects.filter(schedule_id=broadcast.schedule_id).update(
            active=False)
        # We have to generate this function outside of this context otherwise it will be
        # bound to the name broadcast which changes over the course of iteration
        transaction.on_commit(
            get_refresh_alert_schedule_instances_call(broadcast))

    for broadcast in _get_active_scheduled_broadcasts(domain,
                                                      survey_only=survey_only):
        TimedSchedule.objects.filter(schedule_id=broadcast.schedule_id).update(
            active=False)
        # We have to generate this function outside of this context otherwise it will be
        # bound to the name broadcast which changes over the course of iteration
        transaction.on_commit(
            get_refresh_timed_schedule_instances_call(broadcast))

    for rule in _get_active_scheduling_rules(domain, survey_only=survey_only):
        """
        Deactivating a scheduling rule involves only deactivating the schedule, and
        leaving the rule active. See ConditionalAlertListView.get_activate_ajax_response
        for more information.
        """
        with transaction.atomic():
            schedule = rule.get_messaging_rule_schedule()
            if isinstance(schedule, AlertSchedule):
                AlertSchedule.objects.filter(
                    schedule_id=schedule.schedule_id).update(active=False)
            elif isinstance(schedule, TimedSchedule):
                TimedSchedule.objects.filter(
                    schedule_id=schedule.schedule_id).update(active=False)
            else:
                raise TypeError("Expected AlertSchedule or TimedSchedule")

            initiate_messaging_rule_run(rule)
Esempio n. 10
0
    def get_activate_ajax_response(self, active_flag, rule):
        """
        When we deactivate a conditional alert from the UI, we are only
        deactivating the schedule that sends the content. The rule itself
        stays active.
        This is because we want to be keeping all the schedule instances
        up to date (though inactive), so that if the schedule is reactivated,
        we don't send a large quantity of stale messages.
        """
        with transaction.atomic():
            schedule = rule.get_messaging_rule_schedule()
            if active_flag and not self.can_use_inbound_sms and schedule.memoized_uses_sms_survey:
                return HttpResponseBadRequest(
                    "Cannot create or edit survey reminders because subscription "
                    "does not have access to inbound SMS"
                )

            if active_flag and (rule.references_parent_case or schedule.references_parent_case):
                return HttpResponseBadRequest(
                    "Cannot reactivate alerts that reference parent case properties"
                )

            if active_flag and (schedule.memoized_uses_ivr_survey or schedule.memoized_uses_sms_callback):
                return HttpResponseBadRequest(
                    "Cannot activate alerts which use IVR or SMS Callback use cases since they "
                    "are no longer supported."
                )

            schedule.active = active_flag
            schedule.save()
            initiate_messaging_rule_run(self.domain, rule.pk)

        return JsonResponse({
            'status': 'success',
            'rule': self._format_rule_for_json(rule),
        })
Esempio n. 11
0
def _deactivate_schedules(domain, survey_only=False):
    """
    The subscription changes are executed within a transaction, so
    we need to make sure any celery tasks only get started after the
    transaction commits.
    """
    from corehq.messaging.tasks import initiate_messaging_rule_run

    for broadcast in _get_active_immediate_broadcasts(domain, survey_only=survey_only):
        AlertSchedule.objects.filter(schedule_id=broadcast.schedule_id).update(active=False)
        # We have to generate this function outside of this context otherwise it will be
        # bound to the name broadcast which changes over the course of iteration
        transaction.on_commit(get_refresh_alert_schedule_instances_call(broadcast))

    for broadcast in _get_active_scheduled_broadcasts(domain, survey_only=survey_only):
        TimedSchedule.objects.filter(schedule_id=broadcast.schedule_id).update(active=False)
        # We have to generate this function outside of this context otherwise it will be
        # bound to the name broadcast which changes over the course of iteration
        transaction.on_commit(get_refresh_timed_schedule_instances_call(broadcast))

    for rule in _get_active_scheduling_rules(domain, survey_only=survey_only):
        """
        Deactivating a scheduling rule involves only deactivating the schedule, and
        leaving the rule active. See ConditionalAlertListView.get_activate_ajax_response
        for more information.
        """
        with transaction.atomic():
            schedule = rule.get_messaging_rule_schedule()
            if isinstance(schedule, AlertSchedule):
                AlertSchedule.objects.filter(schedule_id=schedule.schedule_id).update(active=False)
            elif isinstance(schedule, TimedSchedule):
                TimedSchedule.objects.filter(schedule_id=schedule.schedule_id).update(active=False)
            else:
                raise TypeError("Expected AlertSchedule or TimedSchedule")

            initiate_messaging_rule_run(domain.name, rule.pk)
    def handle(self, domain, filename, **options):
        domain_obj = Domain.get_by_name(domain)
        if domain_obj is None:
            raise CommandError("Project space '%s' not found" % domain)

        if not project_is_on_new_reminders(domain_obj):
            raise CommandError(
                "Project space '%s' does not have new reminders enabled" %
                domain)

        json_rules = []
        with open_for_json_read(filename) as f:
            for line in f:
                json_rules.append(json.loads(line))

        print("Importing %s rules..." % len(json_rules))

        rules = []
        with transaction.atomic():
            for entry in json_rules:
                json_rule = SimpleSchedulingRule(entry['rule'])

                schedule_type = entry['schedule']['schedule_type']
                if schedule_type == SIMPLE_SMS_DAILY_SCHEDULE_WITH_TIME:
                    json_schedule = SimpleSMSDailyScheduleWithTime(
                        entry['schedule'])
                    schedule = TimedSchedule.create_simple_daily_schedule(
                        domain,
                        TimedEvent(time=json_schedule.time),
                        SMSContent(message=json_schedule.message),
                        total_iterations=json_schedule.total_iterations,
                        start_offset=json_schedule.start_offset,
                        start_day_of_week=json_schedule.start_day_of_week,
                        extra_options=json_schedule.extra_options.to_json(),
                        repeat_every=json_schedule.repeat_every,
                    )
                elif schedule_type == SIMPLE_SMS_ALERT_SCHEDULE:
                    json_schedule = SimpleSMSAlertSchedule(entry['schedule'])
                    schedule = AlertSchedule.create_simple_alert(
                        domain,
                        SMSContent(message=json_schedule.message),
                        extra_options=json_schedule.extra_options.to_json(),
                    )
                else:
                    raise CommandError("Unexpected schedule_type: %s" %
                                       schedule_type)

                rule = AutomaticUpdateRule.objects.create(
                    domain=domain,
                    name=json_rule.name,
                    case_type=json_rule.case_type,
                    active=True,
                    filter_on_server_modified=False,
                    workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                )

                for criterion in json_rule.criteria:
                    rule.add_criteria(
                        MatchPropertyDefinition,
                        property_name=criterion.property_name,
                        property_value=criterion.property_value,
                        match_type=criterion.match_type,
                    )

                rule.add_action(
                    CreateScheduleInstanceActionDefinition,
                    alert_schedule_id=schedule.schedule_id if isinstance(
                        schedule, AlertSchedule) else None,
                    timed_schedule_id=schedule.schedule_id if isinstance(
                        schedule, TimedSchedule) else None,
                    recipients=json_rule.recipients,
                    reset_case_property_name=json_rule.
                    reset_case_property_name,
                    start_date_case_property=json_rule.
                    start_date_case_property,
                    specific_start_date=json_rule.specific_start_date,
                    scheduler_module_info=json_rule.scheduler_module_info.
                    to_json(),
                )

                rules.append(rule)

        print("Import complete. Starting instance refresh tasks...")

        for rule in rules:
            initiate_messaging_rule_run(rule.domain, rule.pk)

        print("Done.")
 def refresh_schedule_instances(self):
     initiate_messaging_rule_run(self.rule.domain, self.rule.pk)
Esempio n. 14
0
    def upload(self, workbook):
        self.msgs = []
        success_count = 0
        worksheet = workbook.get_worksheet(title=self.sheet_name)

        errors = self.get_worksheet_errors(worksheet)
        if errors:
            return errors

        # Most rules are represented by a single row, but rules with custom schedules have one row per event.
        # Read through the worksheet, grouping rows by rule id and caching rule definitions.
        condensed_rows = defaultdict(list)
        rules_by_id = {}

        for index, row in enumerate(worksheet,
                                    start=2):  # one-indexed, plus header row
            if not row.get('id', None):
                self.msgs.append(
                    (messages.error,
                     _("Row {index} in '{sheet_name}' sheet is missing "
                       "an id.").format(index=index,
                                        sheet_name=self.sheet_name)))
                continue

            if row['id'] in condensed_rows:
                # This is the 2nd (or 3rd, 4th, ...) row for a rule we've already seen
                condensed_rows[row['id']].append(row)
                continue

            try:
                rule = AutomaticUpdateRule.objects.get(
                    pk=row['id'],
                    domain=self.domain,
                    workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                    deleted=False,
                )
            except AutomaticUpdateRule.DoesNotExist:
                self.msgs.append((
                    messages.error,
                    _("""Could not find rule for row {index} in '{sheet_name}' sheet, """
                      """with id {id}""").format(index=index,
                                                 id=row['id'],
                                                 sheet_name=self.sheet_name)))
                continue

            if rule.locked_for_editing:
                self.msgs.append((
                    messages.error,
                    _("Row {index} in '{sheet_name}' sheet, with rule id {id}, "
                      "is currently processing and cannot be updated.").format(
                          index=index,
                          id=row['id'],
                          sheet_name=self.sheet_name)))
                continue

            if not isinstance(
                    rule.get_messaging_rule_schedule().memoized_events[0].
                    content, SMSContent):
                self.msgs.append((
                    messages.error,
                    _("Row {index} in '{sheet_name}' sheet, with rule id {id}, "
                      "does not use SMS content.").format(
                          index=index,
                          id=row['id'],
                          sheet_name=self.sheet_name)))
                continue

            rules_by_id[row['id']] = rule
            condensed_rows[row['id']].append(row)

        # Update the condensed set of rules
        for rule_id, rows in condensed_rows.items():
            rule = rules_by_id[rule_id]
            schedule = rule.get_messaging_rule_schedule()
            send_frequency = ScheduleForm.get_send_frequency_by_ui_type(
                schedule.ui_type)

            if send_frequency in (ScheduleForm.SEND_CUSTOM_DAILY,
                                  ScheduleForm.SEND_CUSTOM_IMMEDIATE):
                # Check that user provided one row for each event in the custom schedule
                all_events = rule.get_messaging_rule_schedule().memoized_events
                expected = len(
                    [e for e in all_events if self.event_is_relevant(e)])
                actual = len(rows)
                if expected != actual and actual != len(all_events):
                    self.msgs.append((
                        messages.error,
                        _("Could not update rule with id {id} in '{sheet_name}' "
                          "sheet: expected {expected} row(s) but found "
                          "{actual}.").format(id=rule.id,
                                              sheet_name=self.sheet_name,
                                              expected=expected,
                                              actual=actual)))
                    continue

            with transaction.atomic():
                try:
                    dirty = self.update_rule(rule, rows)
                except RuleUpdateError as e:
                    self.msgs.append((
                        messages.error,
                        _("Error updating rule with id {id} in '{sheet_name}' "
                          "sheet: {detail}").format(id=rule.id,
                                                    sheet_name=self.sheet_name,
                                                    detail=str(e))))
                    continue

                if dirty:
                    rule.save()
                    initiate_messaging_rule_run(rule)
                    success_count += 1

        self.msgs.append(
            (messages.success,
             _("Updated {count} rule(s) in '{sheet_name}' sheet").format(
                 count=success_count, sheet_name=self.sheet_name)))

        return self.msgs
    def handle(self, domain, filename, **options):
        domain_obj = Domain.get_by_name(domain)
        if domain_obj is None:
            raise CommandError("Project space '%s' not found" % domain)

        json_rules = []
        with open_for_json_read(filename) as f:
            for line in f:
                json_rules.append(json.loads(line))

        print("Importing %s rules..." % len(json_rules))

        rules = []
        with transaction.atomic():
            for entry in json_rules:
                json_rule = SimpleSchedulingRule(entry['rule'])

                schedule_type = entry['schedule']['schedule_type']
                if schedule_type == SIMPLE_SMS_DAILY_SCHEDULE_WITH_TIME:
                    json_schedule = SimpleSMSDailyScheduleWithTime(entry['schedule'])
                    schedule = TimedSchedule.create_simple_daily_schedule(
                        domain,
                        TimedEvent(time=json_schedule.time),
                        SMSContent(message=json_schedule.message),
                        total_iterations=json_schedule.total_iterations,
                        start_offset=json_schedule.start_offset,
                        start_day_of_week=json_schedule.start_day_of_week,
                        extra_options=json_schedule.extra_options.to_json(),
                        repeat_every=json_schedule.repeat_every,
                    )
                elif schedule_type == SIMPLE_SMS_ALERT_SCHEDULE:
                    json_schedule = SimpleSMSAlertSchedule(entry['schedule'])
                    schedule = AlertSchedule.create_simple_alert(
                        domain,
                        SMSContent(message=json_schedule.message),
                        extra_options=json_schedule.extra_options.to_json(),
                    )
                else:
                    raise CommandError("Unexpected schedule_type: %s" % schedule_type)

                rule = AutomaticUpdateRule.objects.create(
                    domain=domain,
                    name=json_rule.name,
                    case_type=json_rule.case_type,
                    active=True,
                    filter_on_server_modified=False,
                    workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                )

                for criterion in json_rule.criteria:
                    rule.add_criteria(
                        MatchPropertyDefinition,
                        property_name=criterion.property_name,
                        property_value=criterion.property_value,
                        match_type=criterion.match_type,
                    )

                rule.add_action(
                    CreateScheduleInstanceActionDefinition,
                    alert_schedule_id=schedule.schedule_id if isinstance(schedule, AlertSchedule) else None,
                    timed_schedule_id=schedule.schedule_id if isinstance(schedule, TimedSchedule) else None,
                    recipients=json_rule.recipients,
                    reset_case_property_name=json_rule.reset_case_property_name,
                    start_date_case_property=json_rule.start_date_case_property,
                    specific_start_date=json_rule.specific_start_date,
                    scheduler_module_info=json_rule.scheduler_module_info.to_json(),
                )

                rules.append(rule)

        print("Import complete. Starting instance refresh tasks...")

        for rule in rules:
            initiate_messaging_rule_run(rule.domain, rule.pk)

        print("Done.")