示例#1
0
def _update_item_status(
    s_item: ScheduledOperation,
    run_result: List[str],
    debug: bool,
):
    """Update the status of the scheduled item.

    :param s_item: Scheduled item

    :return: Nothing
    """
    now = datetime.now(pytz.timezone(settings.TIME_ZONE))
    if run_result is None:
        s_item.status = ScheduledOperation.STATUS_DONE_ERROR
    else:
        if s_item.execute_until and s_item.execute_until > now:
            # There is a second date/time and is not passed yet!
            s_item.status = ScheduledOperation.STATUS_PENDING
            # Update exclude values
            s_item.exclude_values.extend(run_result)
        else:
            s_item.status = ScheduledOperation.STATUS_DONE

    # Save the new status in the DB
    s_item.save()

    if debug:
        logger.info('Status set to %s', s_item.status)
示例#2
0
文件: tasks_ops.py 项目: ubc/ontask_b
def schedule_task(s_item: models.ScheduledOperation):
    """Create the task corresponding to the given scheduled item.

    :param s_item: Scheduled operation item being processed.
    :return: Creates a new task in the  PeriodicTask table

    If the s_item already contains a pointer to a task in PeriodicTasks, it
    is canceled.

    If the s_item status is not PENDING, no new task is created.
    """
    enabled = True
    if s_item.task:
        # Preserve the enabled flag in the old task, otherwise is reset
        enabled = s_item.task.enabled
    s_item.delete_task()

    msg = s_item.are_times_valid()
    if msg:
        raise errors.OnTaskScheduleIncorrectTimes(msg)

    # Case of a single execution in the future
    if s_item.execute and not s_item.frequency and not s_item.execute_until:
        # Case 5
        clocked_item, __ = ClockedSchedule.objects.get_or_create(
            clocked_time=s_item.execute)
        task_id = PeriodicTask.objects.create(
            clocked=clocked_item,
            one_off=True,
            name=ONTASK_SCHEDULED_TASK_NAME_TEMPLATE.format(s_item.id),
            task='ontask.tasks.scheduled_ops.execute_scheduled_operation',
            args=json.dumps([s_item.id]),
            enabled = enabled)
    else:
        # Cases 3, 4, 7 and 8: crontab execution
        crontab_items = s_item.frequency.split()
        crontab_item, __ = CrontabSchedule.objects.get_or_create(
            minute=crontab_items[0],
            hour=crontab_items[1],
            day_of_week=crontab_items[2],
            day_of_month=crontab_items[3],
            month_of_year=crontab_items[4])

        task_id = PeriodicTask.objects.create(
            crontab=crontab_item,
            name=ONTASK_SCHEDULED_TASK_NAME_TEMPLATE.format(s_item.id),
            task='ontask.tasks.scheduled_ops.execute_scheduled_operation',
            args=json.dumps([s_item.id]),
            enabled = enabled)

    models.ScheduledOperation.objects.filter(pk=s_item.id).update(task=task_id)
    s_item.refresh_from_db(fields=['task'])
示例#3
0
def _update_item_status(s_item: models.ScheduledOperation):
    """Update the status of the scheduled item.

    :param s_item: Scheduled item
    :return: Nothing
    """
    now = datetime.now(pytz.timezone(settings.TIME_ZONE))
    if s_item.frequency and (not s_item.execute_until
                             or now < s_item.execute_until):
        new_status = models.scheduler.STATUS_PENDING
    else:
        new_status = models.scheduler.STATUS_DONE

    # Save the new status in the DB
    models.ScheduledOperation.objects.filter(pk=s_item.id).update(
        status=new_status)
    s_item.refresh_from_db(fields=['status'])

    if settings.DEBUG:
        CELERY_LOGGER.info('Status set to %s', s_item.status)
    def test_scheduled_json_action(self):
        """Create a scheduled send list action and execute it."""
        token = 'fake token'

        OnTaskSharedState.json_outbox = []
        settings.EXECUTE_ACTION_JSON_TRANSFER = False

        user = get_user_model().objects.get(email='*****@*****.**')

        # User must exist
        self.assertIsNotNone(user, 'User [email protected] not found')
        action = Action.objects.get(name='send json')

        scheduled_item = ScheduledOperation(
            user=user,
            name='JSON scheduled action',
            action=action,
            execute=datetime.now(pytz.timezone(settings.TIME_ZONE)).replace(
                second=0),
            status=ScheduledOperation.STATUS_PENDING,
            item_column=action.workflow.columns.get(name='email'),
            payload={'token': token})
        scheduled_item.save()

        # Execute the scheduler
        tasks.execute_scheduled_actions_task(True)

        scheduled_item.refresh_from_db()
        json_outbox = OnTaskSharedState.json_outbox
        assert scheduled_item.status == ScheduledOperation.STATUS_DONE
        assert len(json_outbox) == 3
        assert all(item['target'] == action.target_url for item in json_outbox)
        assert all(token in item['auth'] for item in json_outbox)
    def test_scheduled_email_action(self):
        """Create a scheduled send email action and execute it."""

        user = get_user_model().objects.get(email='*****@*****.**')

        # User must exist
        self.assertIsNotNone(user, 'User [email protected] not found')
        action = Action.objects.get(name='send email')

        scheduled_item = ScheduledOperation(
            user=user,
            name='send email action',
            action=action,
            execute=datetime.now(pytz.timezone(settings.TIME_ZONE)).replace(
                second=0),
            status=ScheduledOperation.STATUS_PENDING,
            item_column=action.workflow.columns.get(name='email'),
            payload={
                'subject': 'Email subject',
                'cc_email': '',
                'bcc_email': '',
                'send_confirmation': False,
                'track_read': False})

        scheduled_item.save()

        # Execute the scheduler
        tasks.execute_scheduled_actions_task(True)

        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_DONE
        assert len(mail.outbox) == 2
        assert 'Hi Student Two' in mail.outbox[0].body
        assert 'Hi Student Three' in mail.outbox[1].body
    def test_scheduled_send_list_action(self):
        """Create a scheduled send list action and execute it."""

        user = get_user_model().objects.get(email='*****@*****.**')

        # User must exist
        self.assertIsNotNone(user, 'User [email protected] not found')
        action = Action.objects.get(name='send list')

        scheduled_item = ScheduledOperation(
            user=user,
            name='send list scheduled action',
            action=action,
            execute=datetime.now(pytz.timezone(settings.TIME_ZONE)).replace(
                second=0),
            status=ScheduledOperation.STATUS_PENDING,
            payload={
                'email_to': '*****@*****.**',
                'subject': 'Action subject',
                'cc_email': '',
                'bcc_email': ''})
        scheduled_item.save()

        # Execute the scheduler
        tasks.execute_scheduled_actions_task(True)

        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_DONE
        assert len(mail.outbox) == 1
        assert (
            '[email protected], [email protected]' in mail.outbox[0].body)
示例#7
0
    def finish(
        self,
        request: http.HttpRequest,
        payload: SessionPayload,
        schedule_item: models.ScheduledOperation = None,
    ) -> Optional[http.HttpResponse]:
        """Finalize the creation of a scheduled operation.

        All required data is passed through the payload.

        :param request: Request object received
        :param schedule_item: ScheduledOperation item being processed. If None,
        it has to be extracted from the information in the payload.
        :param payload: Dictionary with all the required data coming from
        previous requests.
        :return: Http Response
        """
        s_item_id = payload.pop('schedule_id', None)
        schedule_item = None
        if s_item_id:
            # Get the item being processed
            if not schedule_item:
                schedule_item = models.ScheduledOperation.objects.filter(
                    id=s_item_id).first()
            if not schedule_item:
                messages.error(request,
                               _('Incorrect request for operation scheduling'))
                return redirect('action:index')
        else:
            action = models.Action.objects.get(pk=payload.pop('action_id'))
            payload['workflow'] = action.workflow
            payload['action'] = action

        # Remove some parameters from the payload
        for key in [
                'button_label',
                'valuerange',
                'step',
                'prev_url',
                'post_url',
                'confirm_items',
                'action_id',
                'page_title',
        ]:
            payload.pop(key, None)

        try:
            schedule_item = self.create_or_update(request.user,
                                                  payload.get_store(),
                                                  schedule_item)
        except Exception as exc:
            messages.error(
                request,
                str(_('Unable to create scheduled operation: {0}')).format(
                    str(exc)))
            return redirect('action:index')

        schedule_item.log(models.Log.SCHEDULE_EDIT)

        # Reset object to carry action info throughout dialogs
        SessionPayload.flush(request.session)

        # Successful processing.
        tdelta = create_timedelta_string(schedule_item.execute,
                                         schedule_item.frequency,
                                         schedule_item.execute_until)
        return render(request, 'scheduler/schedule_done.html', {
            'tdelta': tdelta,
            's_item': schedule_item
        })
    def test_scheduled_incremental_email_action(self):
        """Test an incremental scheduled action."""
        # Modify the data table so that initially all records have registered
        # equal to alse
        workflow = Workflow.objects.all().first()
        with connection.cursor() as cursor:
            query = sql.SQL('UPDATE {0} SET {1} = false').format(
                sql.Identifier(workflow.get_data_frame_table_name()),
                sql.Identifier('registered'))
            cursor.execute(query)

        user = get_user_model().objects.get(email='*****@*****.**')

        # User must exist
        self.assertIsNotNone(user, 'User [email protected] not found')
        action = Action.objects.get(name='send email incrementally')

        now = datetime.now(pytz.timezone(settings.TIME_ZONE)).replace(second=0)
        scheduled_item = ScheduledOperation(
            user=user,
            name='send email action incrementally',
            action=action,
            execute=now,
            execute_until=now + timedelta(hours=1),
            status=ScheduledOperation.STATUS_PENDING,
            item_column=action.workflow.columns.get(name='email'),
            payload={
                'subject': 'Email subject',
                'cc_email': '',
                'bcc_email': '',
                'send_confirmation': False,
                'track_read': False})
        scheduled_item.save()

        # Execute the scheduler for the first time
        tasks.execute_scheduled_actions_task(True)

        # Event stil pending, with no values in exclude values
        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_PENDING
        assert scheduled_item.exclude_values == []

        # Modify one of the values in the matrix
        with connection.cursor() as cursor:
            query = sql.SQL(
                'UPDATE {0} SET {1} = true WHERE {2} = {3}').format(
                    sql.Identifier(workflow.get_data_frame_table_name()),
                    sql.Identifier('registered'),
                    sql.Identifier('email'),
                    sql.Placeholder())
            cursor.execute(query, ['*****@*****.**'])

        # Execute the scheduler for the first time
        tasks.execute_scheduled_actions_task(True)

        # Event stil pending, with no values in exclude values
        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_PENDING
        assert scheduled_item.exclude_values == ['*****@*****.**']

        # Modify one of the values in the matrix
        with connection.cursor() as cursor:
            query = sql.SQL(
                'UPDATE {0} SET {1} = true WHERE {2} = {3}').format(
                    sql.Identifier(workflow.get_data_frame_table_name()),
                    sql.Identifier('registered'),
                    sql.Identifier('email'),
                    sql.Placeholder())
            cursor.execute(query, ['*****@*****.**'])

        # Execute the scheduler for the first time
        tasks.execute_scheduled_actions_task(True)

        # Event stil pending, with no values in exclude values
        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_PENDING
        assert scheduled_item.exclude_values == [
            '*****@*****.**',
            '*****@*****.**']

        # Modify one of the values in the matrix
        with connection.cursor() as cursor:
            query = sql.SQL(
                'UPDATE {0} SET {1} = true WHERE {2} = {3}').format(
                    sql.Identifier(workflow.get_data_frame_table_name()),
                    sql.Identifier('registered'),
                    sql.Identifier('email'),
                    sql.Placeholder())
            cursor.execute(query, ['*****@*****.**'])

        # Execute the scheduler for the first time
        tasks.execute_scheduled_actions_task(True)

        # Event stil pending, with no values in exclude values
        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_PENDING
        assert scheduled_item.exclude_values == [
            '*****@*****.**',
            '*****@*****.**',
            '*****@*****.**']

        # Execute the scheduler for the first time
        tasks.execute_scheduled_actions_task(True)

        # Event stil pending, with no values in exclude values
        scheduled_item.refresh_from_db()
        assert scheduled_item.status == ScheduledOperation.STATUS_PENDING
        assert scheduled_item.exclude_values == [
            '*****@*****.**',
            '*****@*****.**',
            '*****@*****.**']
示例#9
0
def delete_item(s_item: models.ScheduledOperation):
    """Delete a scheduled operation and log the event."""
    s_item.log(models.Log.SCHEDULE_DELETE)
    s_item.delete()
示例#10
0
    def instantiate_or_update(
        self,
        validated_data,
        action,
        execute,
        item_column,
        exclude_values,
        payload,
        scheduled_obj=None,
    ):
        """Instantiate or update the object of class ScheduledOperation.

        Given the validated data and a set of parameters that have been
        validated, instantiate or update the object of class ScheduledOperation.

        :param validated_data: Data obtained by the serializer

        :param action: Action object

        :param execute: Execution date/time

        :param item_column: Item column object (if given)

        :param exclude_values: List of values from item_column to exluce

        :param payload: JSON object

        :param scheduled_obj: Object to instantiate or update

        :return: instantiated object
        """
        if not scheduled_obj:
            scheduled_obj = ScheduledOperation()

        scheduled_obj.user = self.context['request'].user
        scheduled_obj.name = validated_data['name']
        scheduled_obj.description_text = validated_data.get('description_text')
        scheduled_obj.action = action
        scheduled_obj.execute = execute
        scheduled_obj.item_column = item_column
        scheduled_obj.exclude_values = exclude_values
        scheduled_obj.payload = payload
        scheduled_obj.status = ScheduledOperation.STATUS_PENDING

        scheduled_obj.save()
        return scheduled_obj
示例#11
0
def finish_scheduling(
    request: HttpRequest,
    schedule_item: ScheduledOperation = None,
    payload: Dict = None,
):
    """Finalize the creation of a scheduled action.

    All required data is passed through the payload.

    :param request: Request object received

    :param schedule_item: ScheduledOperation item being processed. If None,
    it has to be extracted from the information in the payload.

    :param payload: Dictionary with all the required data coming from
    previous requests.

    :return:
    """
    # Get the payload from the session if not given
    if payload is None:
        payload = request.session.get(action_session_dictionary)

        # If there is no payload, something went wrong.
        if payload is None:
            # Something is wrong with this execution. Return to action table.
            messages.error(
                request,
                _('Incorrect action scheduling invocation.'))
            return redirect('action:index')

    # Get the scheduled item if needed
    s_item_id = payload.pop('schedule_id', None)
    action = Action.objects.get(pk=payload.pop('action_id'))
    column_name = payload.pop('item_column', None)
    column = None
    if column_name:
        column = action.workflow.columns.get(name=column_name)

    # Clean up some parameters from the payload
    payload = {
        key: payload[key]
        for key in payload if key not in [
            'button_label',
            'valuerange',
            'step',
            'prev_url',
            'post_url',
            'confirm_items']}

    # Create the payload to record the event in the log
    log_payload = payload.copy()

    if s_item_id:
        # Get the item being processed
        if not schedule_item:
            schedule_item = ScheduledOperation.objects.filter(
                id=s_item_id).first()
        if not schedule_item:
            messages.error(
                None,
                _('Incorrect request in action scheduling'))
            return redirect('action:index')
        schedule_item.name = payload.pop('name')
        schedule_item.description_text = payload.pop('description_text')
        schedule_item.item_column = column
        schedule_item.execute = parse_datetime(payload.pop('execute'))
        schedule_item.execute_until = parse_datetime(
            payload.pop('execute_until'))
        schedule_item.exclude_values = payload.pop('exclude_values', [])
    else:
        schedule_item = ScheduledOperation(
            user=request.user,
            action=action,
            name=payload.pop('name'),
            description_text=payload.pop('description_text'),
            operation_type=ScheduledOperation.ACTION_RUN,
            item_column=column,
            execute=parse_datetime(payload.pop('execute')),
            execute_until=parse_datetime(payload.pop('execute_until')),
            exclude_values=payload.pop('exclude_values', []))

    # Check for exclude
    schedule_item.status = ScheduledOperation.STATUS_PENDING
    schedule_item.payload = payload
    schedule_item.save()

    # Create the payload to record the event in the log
    log_type = LOG_TYPE_DICT.get(schedule_item.action.action_type)
    if not log_type:
        messages.error(
            request,
            _('This type of actions cannot be scheduled'))
        return redirect('action:index')
    schedule_item.log(log_type)

    # Reset object to carry action info throughout dialogs
    set_action_payload(request.session)
    request.session.save()

    # Successful processing.
    is_executing, tdelta = create_timedelta_string(
        schedule_item.execute,
        schedule_item.execute_until)
    return render(
        request,
        'scheduler/schedule_done.html',
        {
            'is_executing': is_executing,
            'tdelta': tdelta,
            's_item': schedule_item})