示例#1
0
def action_index(
    request: HttpRequest,
    wid: Optional[int] = None,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Show all the actions attached to the workflow.

    :param request: HTTP Request

    :param pk: Primary key of the workflow object to use

    :return: HTTP response
    """
    # Reset object to carry action info throughout dialogs
    set_action_payload(request.session)
    request.session.save()

    return render(
        request,
        'action/index.html',
        {
            'workflow': workflow,
            'table': ActionTable(workflow.actions.all(), orderable=False),
        },
    )
def run_email_done(
    request: HttpRequest,
    action_info: Optional[EmailPayload] = None,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Create the log object, queue the operation request and render done.

    :param request: HTTP request (GET)
    :param action_info: Dictionary containing all the required parameters. If
    empty, the dictionary is taken from the session.
    :return: HTTP response
    """
    # Get the payload from the session if not given
    action_info = get_or_set_action_info(request.session,
                                         EmailPayload,
                                         action_info=action_info)
    if action_info is None:
        # Something is wrong with this execution. Return to action table.
        messages.error(request, _('Incorrect email action invocation.'))
        return redirect('action:index')

    # Get the information from the payload
    action = workflow.actions.filter(pk=action_info['action_id']).first()
    if not action:
        return redirect('home')

    # Log the event
    log_item = Log.objects.register(
        request.user, Log.SCHEDULE_EMAIL_EXECUTE, action.workflow, {
            'action': action.name,
            'action_id': action.id,
            'from_email': request.user.email,
            'subject': action_info['subject'],
            'cc_email': action_info['cc_email'],
            'bcc_email': action_info['bcc_email'],
            'send_confirmation': action_info['send_confirmation'],
            'track_read': action_info['track_read'],
            'exported_workflow': action_info['export_wf'],
            'exclude_values': action_info['exclude_values'],
            'item_column': action_info['item_column'],
            'status': 'Preparing to execute',
        })

    # Update the last_execution_log
    action.last_executed_log = log_item
    action.save()

    # Send the emails!
    run_task.delay(request.user.id, log_item.id, action_info.get_store())

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

    # Successful processing.
    return render(request, 'action/action_done.html', {
        'log_id': log_item.id,
        'download': action_info['export_wf']
    })
示例#3
0
def zip_action(
    req: HttpRequest,
    pk: int,
    workflow: Optional[Workflow] = None,
    action: Optional[Action] = None,
) -> HttpResponse:
    """Request data to create a zip file.

    Form asking for participant column, user file name column, file suffix,
    if it is a ZIP for Moodle and confirm users step.

    :param req: HTTP request (GET)
    :param pk: Action key
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_or_set_action_info(req.session,
                                         ZipPayload,
                                         initial_values={
                                             'action_id':
                                             action.id,
                                             'prev_url':
                                             reverse('action:zip_action',
                                                     kwargs={'pk': action.id}),
                                             'post_url':
                                             reverse('action:zip_done'),
                                         })

    # Create the form to ask for the email subject and other information
    form = ZipActionForm(
        req.POST or None,
        column_names=[col.name for col in workflow.columns.all()],
        action=action,
        form_info=action_info,
    )

    if req.method == 'POST' and form.is_valid():
        if action_info['confirm_items']:
            # Add information to the session object to execute the next pages
            action_info['button_label'] = ugettext('Create ZIP')
            action_info['valuerange'] = 2
            action_info['step'] = 2
            set_action_payload(req.session, action_info.get_store())

            return redirect('action:item_filter')

        # Go straight to the final step.
        return run_zip_done(req, action_info=action_info, workflow=workflow)

    # Render the form
    return render(
        req, 'action/action_zip_step1.html', {
            'action': action,
            'num_msgs': action.get_rows_selected(),
            'form': form,
            'valuerange': range(2)
        })
def run_json_list_action(
    req: HttpRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Request data to send JSON objects.

    Form asking for token and export wf

    :param req: HTTP request (GET)
    :param workflow: workflow being processed
    :param action: Action begin run
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = JSONListPayload({'action_id': action.id})

    # Create the form to ask for the email subject and other information
    form = JSONListActionForm(req.POST or None, form_info=action_info)

    if req.method == 'POST' and form.is_valid():
        # Log the event
        log_item = Log.objects.register(
            req.user, Log.SCHEDULE_JSON_EXECUTE, action.workflow, {
                'action': action.name,
                'action_id': action.id,
                'exported_workflow': action_info['export_wf'],
                'status': 'Preparing to execute',
                'target_url': action.target_url
            })

        # Update the last_execution_log
        action.last_executed_log = log_item
        action.save()

        # Send the objects
        run_task.delay(req.user.id, log_item.id, action_info.get_store())

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

        # Successful processing.
        return render(req, 'action/action_done.html', {
            'log_id': log_item.id,
            'download': action_info['export_wf']
        })

    # Render the form
    return render(
        req, 'action/request_json_list_data.html', {
            'action': action,
            'num_msgs': action.get_rows_selected(),
            'form': form,
            'valuerange': range(2),
            'rows_all_false': action.get_row_all_false_count()
        })
def run_email_action(
    req: HttpRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Request data to send emails.

    Form asking for subject line, email column, etc.
    :param req: HTTP request (GET)
    :param workflow: workflow being processed
    :param action: Action being run
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_or_set_action_info(req.session,
                                         EmailPayload,
                                         initial_values={
                                             'action_id':
                                             action.id,
                                             'prev_url':
                                             reverse('action:run',
                                                     kwargs={'pk': action.id}),
                                             'post_url':
                                             reverse('action:email_done')
                                         })

    # Create the form to ask for the email subject and other information
    form = EmailActionForm(req.POST or None,
                           column_names=[
                               col.name
                               for col in workflow.columns.filter(is_key=True)
                           ],
                           action=action,
                           form_info=action_info)

    # Request is a POST and is valid
    if req.method == 'POST' and form.is_valid():
        if action_info['confirm_items']:
            # Add information to the session object to execute the next pages
            action_info['button_label'] = ugettext('Send')
            action_info['valuerange'] = 2
            action_info['step'] = 2
            set_action_payload(req.session, action_info.get_store())

            return redirect('action:item_filter')

        # Go straight to the final step.
        return run_email_done(req, action_info=action_info, workflow=workflow)

    # Render the form
    return render(
        req, 'action/request_email_data.html', {
            'action': action,
            'num_msgs': action.get_rows_selected(),
            'form': form,
            'valuerange': range(2)
        })
示例#6
0
def save_json_schedule(
    request: HttpRequest,
    action: Action,
    schedule_item: ScheduledOperation,
    op_payload: Dict,
) -> HttpResponse:
    """Create and edit scheduled json actions.

    :param request: Http request being processed

    :param action: Action item related to the schedule

    :param schedule_item: Schedule item or None if it is new

    :param op_payload: dictionary to carry over the request to the next step

    :return: Http response with the rendered page
    """
    # Create the form to ask for the email subject and other information
    form = JSONScheduleForm(
        form_data=request.POST or None,
        action=action,
        instance=schedule_item,
        columns=action.workflow.columns.filter(is_key=True),
        form_info=op_payload)

    # Processing a valid POST request
    if request.method == 'POST' and form.is_valid():
        if op_payload['confirm_items']:
            # Create a dictionary in the session to carry over all the
            # information to execute the next steps
            op_payload['button_label'] = ugettext('Schedule')
            op_payload['valuerange'] = 2
            op_payload['step'] = 2
            set_action_payload(request.session, op_payload)

            return redirect('action:item_filter')

        # Go straight to the final step
        return finish_scheduling(request, schedule_item, op_payload)

    # Render the form
    return render(
        request,
        'scheduler/edit.html',
        {
            'action': action,
            'form': form,
            'now': datetime.now(pytz.timezone(settings.TIME_ZONE)),
            'valuerange': range(2),
        },
    )
示例#7
0
def run_action_item_filter(
    request: HttpRequest,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Offer a select widget to tick items to exclude from selection.

    This is a generic Web function. It assumes that the session object has a
    dictionary with a field stating what objects need to be considered for
    selection. It creates the right web form and then updates in the session
    dictionary the result and proceeds to a URL given also as part of that
    dictionary.

    :param request: HTTP request (GET) with a session object and a dictionary
    with the right parameters. The selected values are stored in the field
    'exclude_values'.

    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_action_payload(request.session)
    if not action_info:
        # Something is wrong with this execution. Return to the action table.
        messages.error(request, _('Incorrect item filter invocation.'))
        return redirect('action:index')

    # Get the information from the payload
    action = Action.objects.get(id=action_info['action_id'])

    form = ValueExcludeForm(request.POST or None,
                            action=action,
                            column_name=action_info['item_column'],
                            form_info=action_info)

    context = {
        'form': form,
        'action': action,
        'button_label': action_info['button_label'],
        'valuerange': range(action_info['valuerange']),
        'step': action_info['step'],
        'prev_step': action_info['prev_url']
    }

    # The post is correct
    if request.method == 'POST' and form.is_valid():
        # Updating the payload in the session
        set_action_payload(request.session, action_info)

        return redirect(action_info['post_url'])

    return render(request, 'action/item_filter.html', context)
示例#8
0
def action_zip_export(
    request: HttpRequest,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Create a zip with the personalised text and return it as response.

    :param request: Request object with a Dictionary with all the required
    information
    :return: Response (download)
    """
    # Get the payload from the session if not given
    action_info = get_or_set_action_info(request.session, ZipPayload)
    if not action_info:
        # Something is wrong with this execution. Return to action table.
        messages.error(request, _('Incorrect ZIP action invocation.'))
        return redirect('action:index')

    # Get the information from the payload
    action = workflow.actions.filter(pk=action_info['action_id']).first()
    if not action:
        return redirect('home')

    # Create the file name template
    if action_info['zip_for_moodle']:
        file_name_template = ('{user_fname}_{part_id}_assignsubmission_file_')
    else:
        if action_info['user_fname_column']:
            file_name_template = '{part_id}_{user_fname}_'
        else:
            file_name_template = '{part_id}'
    if action_info['file_suffix']:
        file_name_template += action_info['file_suffix']
    else:
        file_name_template += 'feedback.html'

    # Create the ZIP with the eval data tuples and return it for download
    sbuf = create_zip(
        create_eval_data_tuple(
            action,
            action_info['item_column'],
            action_info['exclude_values'],
            action_info['user_fname_column'],
        ), action_info['zip_for_moodle'], file_name_template)

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

    return create_response(sbuf)
示例#9
0
def run_json_done(
    request: HttpRequest,
    action_info: Optional[JSONPayload] = None,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Create the log object, queue the operation request, render DONE page.

    :param request: HTTP request (GET)

    :param action_info: Dictionary containing all the required parameters. If
    empty, the dictionary is taken from the session.

    :return: HTTP response
    """
    # Get the payload from the session if not given
    action_info = get_or_set_action_info(request.session,
                                         JSONPayload,
                                         action_info=action_info)
    if action_info is None:
        # Something is wrong with this execution. Return to action table.
        messages.error(request, _('Incorrect JSON action invocation.'))
        return redirect('action:index')

    # Get the information from the payload
    action = workflow.actions.filter(pk=action_info['action_id']).first()
    if not action:
        return redirect('home')

    # Log the event
    log_item = action.log(request.user,
                          Log.ACTION_RUN_JSON,
                          exclude_values=action_info['exclude_values'],
                          item_column=action_info['item_column'],
                          exported_workflow=action_info['export_wf'])

    # Send the objects
    run_task.delay(request.user.id, log_item.id, action_info.get_store())

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

    # Successful processing.
    return render(request, 'action/action_done.html', {
        'log_id': log_item.id,
        'download': action_info['export_wf']
    })
示例#10
0
def run_zip_done(
    request: HttpRequest,
    action_info: Optional[ZipPayload] = None,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Create the zip object, send it for download and render the DONE page.

    :param request: HTTP request (GET)
    :param action_info: Dictionary containing all the required parameters. If
    empty, the dictionary is taken from the session.
    :return: HTTP response
    """
    # Get the payload from the session if not given
    action_info = get_or_set_action_info(request.session,
                                         ZipPayload,
                                         action_info=action_info)
    if action_info is None:
        # Something is wrong with this execution. Return to action table.
        messages.error(request, _('Incorrect ZIP action invocation.'))
        return redirect('action:index')

    # Get the information from the payload
    action = workflow.actions.filter(pk=action_info['action_id']).first()
    if not action:
        return redirect('home')

    # Log the event
    log_item = Log.objects.register(
        request.user, Log.DOWNLOAD_ZIP_ACTION, action.workflow, {
            'action': action.name,
            'action_id': action.id,
            'user_fname_column': action_info['user_fname_column'],
            'item_column': action_info['item_column'],
            'file_suffix': action_info['file_suffix'],
            'zip_for_moodle': action_info['zip_for_moodle'],
            'exclude_values': action_info['exclude_values'],
        })

    # Store the payload in the session for the download part
    set_action_payload(request.session, action_info.get_store())

    # Successful processing.
    return render(request, 'action/action_zip_done.html', {})
示例#11
0
def index(
    request: HttpRequest,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Render the list of actions attached to a workflow.

    :param request: Request object
    :return: HTTP response with the table.
    """
    # Reset object to carry action info throughout dialogs
    set_action_payload(request.session)
    request.session.save()

    # Get the actions
    s_items = ScheduledAction.objects.filter(action__workflow=workflow.id)

    return render(
        request,
        'scheduler/index.html',
        {
            'table': ScheduleActionTable(s_items, orderable=False),
            'workflow': workflow,
        },
    )
示例#12
0
def finish_scheduling(request: HttpRequest,
                      schedule_item: ScheduledAction = 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: ScheduledAction 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 not s_item_id:
        schedule_item = ScheduledAction(
            user=request.user,
            action=action,
            name=payload.pop('name'),
            description_text=payload.pop('description_text'),
            item_column=column,
            execute=parse_datetime(payload.pop('execute')),
            exclude_values=payload.pop('exclude_values', []))
    else:
        # Get the item being processed
        if not schedule_item:
            schedule_item = ScheduledAction.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.exclude_values = payload.pop('exclude_values', [])

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

    # Create the payload to record the event in the log
    if schedule_item.action.action_type == Action.personalized_text:
        log_type = Log.SCHEDULE_EMAIL_EDIT
    elif schedule_item.action.action_type == Action.send_list:
        log_type = Log.SCHEDULE_SEND_LIST_EDIT
    elif schedule_item.action.action_type == Action.personalized_json:
        ivalue = None
        if schedule_item.item_column:
            ivalue = schedule_item.item_column.name
        log_type = Log.SCHEDULE_JSON_EDIT
    elif schedule_item.action.action_type == Action.send_list_json:
        log_type = Log.SCHEDULE_JSON_LIST_EDIT
    elif schedule_item.action.action_type == Action.personalized_canvas_email:
        ivalue = None
        if schedule_item.item_column:
            ivalue = schedule_item.item_column.name
        log_type = Log.SCHEDULE_CANVAS_EMAIL_EDIT
    else:
        messages.error(request, _('This type of actions cannot be scheduled'))
        return redirect('action:index')

    # Create the log
    Log.objects.register(request.user, log_type, schedule_item.action.workflow,
                         log_payload)

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

    # Successful processing.
    return render(
        request, 'scheduler/schedule_done.html', {
            'tdelta': create_timedelta_string(schedule_item.execute),
            's_item': schedule_item
        })
示例#13
0
def edit(
    request: HttpRequest,
    pk: int,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Edit an existing scheduled email action.

    :param request: HTTP request

    :param pk: primary key of the action

    :return: HTTP response
    """
    # Distinguish between creating a new element or editing an existing one
    is_a_new_item = request.path.endswith(reverse(
        'scheduler:create',
        kwargs={'pk': pk}))

    if is_a_new_item:
        action = workflow.actions.filter(pk=pk).filter(
            Q(workflow__user=request.user)
            | Q(workflow__shared=request.user),
        ).first()
        if not action:
            return redirect('home')
        s_item = None
    else:
        # Get the scheduled action from the parameter in the URL
        s_item = ScheduledAction.objects.filter(pk=pk).first()
        if not s_item:
            return redirect('home')
        action = s_item.action

    # Get the payload from the session, and if not, use the given one
    op_payload = request.session.get(action_session_dictionary)
    if not op_payload:
        op_payload = {
            'action_id': action.id,
            'prev_url': reverse(
                'scheduler:create',
                kwargs={'pk': action.id}),
            'post_url': reverse(
                'scheduler:finish_scheduling'),
        }
        if s_item:
            op_payload.update(s_item.payload)
        set_action_payload(request.session, op_payload)
        request.session.save()

    # Verify that celery is running!
    if not celery_is_up():
        messages.error(
            request,
            _(
                'Unable to schedule actions due to a misconfiguration. '
                + 'Ask your system administrator to enable queueing.'))
        return redirect(reverse('action:index'))

    if s_item:
        op_payload['schedule_id'] = s_item.id

    if action.action_type == Action.personalized_text:
        return save_email_schedule(request, action, s_item, op_payload)
    elif action.action_type == Action.send_list:
        return save_send_list_schedule(request, action, s_item, op_payload)
    elif action.action_type == Action.send_list_json:
        return save_send_list_json_schedule(
            request,
            action,
            s_item,
            op_payload)
    elif action.action_type == Action.personalized_canvas_email:
        return save_canvas_email_schedule(
            request,
            action,
            s_item,
            op_payload)
    elif action.action_type == Action.personalized_json:
        return save_json_schedule(request, action, s_item, op_payload)

    # Action type not found, so return to the main table view
    messages.error(
        request,
        _('This action does not support scheduling'))
    return redirect('scheduler:index')
示例#14
0
def run_canvas_email_action(
    req: WSGIRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Request data to send JSON objects.

    Form asking for subject, item column (contains ids to select unique users),
    confirm items (add extra step to drop items), export workflow and
    target_rul (if needed).

    :param req: HTTP request (GET)
    :param workflow: workflow being processed
    :param action: Action begin run
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_or_set_action_info(
        req.session,
        CanvasEmailPayload,
        initial_values={
            'action_id': action.id,
            'prev_url': reverse('action:run', kwargs={'pk': action.id}),
            'post_url': reverse('action:canvas_email_done'),
        },
    )

    # Create the form to ask for the email subject and other information
    form = CanvasEmailActionForm(
        req.POST or None,
        column_names=[
            col.name for col in workflow.columns.filter(is_key=True)
        ],
        action=action,
        form_info=action_info)

    if req.method == 'POST' and form.is_valid():
        # Request is a POST and is valid

        if action_info['confirm_items']:
            # Create a dictionary in the session to carry over all the
            # information to execute the next pages
            action_info['button_label'] = ugettext('Send')
            action_info['valuerange'] = 2
            action_info['step'] = 2
            set_action_payload(req.session, action_info.get_store())
            continue_url = 'action:item_filter'
        else:
            continue_url = 'action:canvas_email_done'

        # Check for the CANVAS token and proceed to the continue_url
        return canvas_get_or_set_oauth_token(req, action_info['target_url'],
                                             continue_url)

    # Render the form
    return render(
        req, 'action/request_canvas_email_data.html', {
            'action': action,
            'num_msgs': action.get_rows_selected(),
            'form': form,
            'valuerange': range(2),
            'rows_all_false': action.get_row_all_false_count()
        })
示例#15
0
def _edit_scheduled_action_run(
    request: HttpRequest,
    s_item: Optional[ScheduledOperation] = None,
    action: Optional[Action] = None,
) -> HttpResponse:
    """Edit a scheduled operation (either new or existing)

    :param request: HTTP request

    :param s_item: Existing schedule item being processed

    :param schedule_type: Schedule operation type, or if empty, it is contained
    in the scheduled_item

    :param workflow: Corresponding workflow for the schedule operation type, or
    if empty, it is contained in the scheduled_item

    :param action: Corresponding action for the schedule operation type, or
    if empty, it is contained in the scheduled_item

    :return: HTTP response
    """
    if s_item:
        action = s_item.action
        exclude_values = s_item.exclude_values
    else:
        exclude_values = []

    # Get the payload from the session, and if not, use the given one
    op_payload = request.session.get(action_session_dictionary)
    if not op_payload:
        op_payload = {
            'action_id':
            action.id,
            'prev_url':
            reverse('scheduler:create_action_run', kwargs={'pk': action.id}),
            'post_url':
            reverse('scheduler:finish_scheduling'),
            'exclude_values':
            exclude_values
        }
        if s_item:
            op_payload.update(s_item.payload)
        set_action_payload(request.session, op_payload)
        request.session.save()

    if s_item:
        op_payload['schedule_id'] = s_item.id

    if action.action_type == Action.PERSONALIZED_TEXT:
        return save_email_schedule(request, action, s_item, op_payload)
    elif action.action_type == Action.SEND_LIST:
        return save_send_list_schedule(request, action, s_item, op_payload)
    elif action.action_type == Action.SEND_LIST_JSON:
        return save_send_list_json_schedule(request, action, s_item,
                                            op_payload)
    elif action.action_type == Action.PERSONALIZED_CANVAS_EMAIL:
        return save_canvas_email_schedule(request, action, s_item, op_payload)
    elif action.action_type == Action.PERSONALIZED_JSON:
        return save_json_schedule(request, action, s_item, op_payload)

    # Action type not found, so return to the main table view
    messages.error(request, _('This action does not support scheduling'))
    return redirect('scheduler:index')