コード例 #1
0
ファイル: models.py プロジェクト: poh-duong/ontask_b
    def update_n_rows_selected(self, column=None, filter_formula=None):
        """
        Given a condition update the number of rows
        for which this condition will have true result. In other words,
        we calculate the number of rows for which the condition is true.

        The function implements two algorithms depending on the condition
        being a filter or not:

        Case 1: Condition is a filter

        - Evaluate the filter and update field.

        - Param filter_formula is ignored

        Case 2: Condition is NOT a filter

        - If there is a filter_formula, create the conjunction together with
        the condition formula.

        :param column: Column that has changed value (None when unknown)
        :param filter_formula: Formula provided by another filter condition
        and to take the conjunction with the condition formula.
        :return: Nothing. Effect recorded in DB objects
        """

        if column and column not in self.columns.all():
            # The column is not part of this condition. Nothing to do
            return

        # Case 1: Condition is a filter
        if self.is_filter:
            self.n_rows_selected = \
                pandas_db.num_rows(self.action.workflow.id, self.formula)
            self.save()

            # Propagate the filter effect to the rest of actions.
            for condition in self.action.conditions.filter(is_filter=False):
                # Update the rest of conditions. The column=None because
                # the filter has changed, thus the condition needs to be
                # reevaluated regardless.
                condition.update_n_rows_selected(column=None,
                                                 filter_formula=self.formula)
            return

        # Case 2: Condition is NOT a filter
        formula = self.formula
        if filter_formula:
            # There is a formula to add to the condition, create a conjunction
            formula = {
                'condition': 'AND',
                'not': False,
                'rules': [filter_formula, self.formula],
                'valid': True
            }
        self.n_rows_selected = pandas_db.num_rows(self.action.workflow.id,
                                                  formula)
        self.save()

        return
コード例 #2
0
ファイル: models.py プロジェクト: leihuagh/ontask_b
    def num_rows(self):
        """
        Number of rows considered by this view
        :return: Number of rows resulting from using the formula
        """
        if not self.nrows:
            self.nrows = \
                pandas_db.num_rows(self.workflow.id, self.formula)

        return self.nrows
コード例 #3
0
def edit_action_out(request, pk):
    """
    View to handle the AJAX form to edit an action (editor, conditions,
    filters, etc).
    :param request: Request object
    :param pk: Action PK
    :return: JSON response
    """

    # Try to get the workflow first
    workflow = get_workflow(request)
    if not workflow:
        return redirect('workflow:index')

    # Get the action and create the form
    try:
        action = Action.objects.filter(
            Q(workflow__user=request.user) |
            Q(workflow__shared=request.user)).distinct().get(pk=pk)
    except ObjectDoesNotExist:
        return redirect('action:index')

    # Create the form
    form = EditActionOutForm(request.POST or None, instance=action)

    # See if the action has a filter or not
    try:
        filter_condition = Condition.objects.get(
            action=action, is_filter=True
        )
    except Condition.DoesNotExist:
        filter_condition = None
    except Condition.MultipleObjectsReturned:
        return render(request, 'error.html',
                      {'message': 'Malfunction detected when retrieving filter '
                                  '(action: {0})'.format(action.id)})

    # Conditions to show in the page as well.
    conditions = Condition.objects.filter(
        action=action, is_filter=False
    ).order_by('created').values('id', 'name')

    # Boolean to find out if there is a table attached to this workflow
    has_data = ops.workflow_has_table(action.workflow)

    # Get the total number of rows in DF and those selected by filter.
    total_rows = workflow.nrows
    if filter_condition:
        action.n_selected_rows = \
            pandas_db.num_rows(action.workflow.id, filter_condition.formula)

    # Context to render the form
    context = {'filter_condition': filter_condition,
               'action': action,
               'conditions': conditions,
               'query_builder_ops': workflow.get_query_builder_ops_as_str(),
               'attribute_names': [x for x in workflow.attributes.keys()],
               'column_names': workflow.get_column_names(),
               'selected_rows': action.n_selected_rows,
               'has_data': has_data,
               'total_rows': total_rows,
               'form': form,
               'vis_scripts': PlotlyHandler.get_engine_scripts()
               }

    # Processing the request after receiving the text from the editor
    if request.method == 'POST':
        # Get the next step
        next_step = request.POST['Submit']

        if form.is_valid():
            content = form.cleaned_data.get('content', None)
            # TODO: Can we detect unused vars only for this invocation?
            # Render the content as a template and catch potential problems.
            # This seems to be only possible if dealing directly with Jinja2
            # instead of Django.
            try:
                render_template(content, {}, action)
            except Exception as e:
                # Pass the django exception to the form (fingers crossed)
                form.add_error(None, e.message)
                return render(request, 'action/edit_out.html', context)

            # Log the event
            logs.ops.put(request.user,
                         'action_update',
                         action.workflow,
                         {'id': action.id,
                          'name': action.name,
                          'workflow_id': workflow.id,
                          'workflow_name': workflow.name,
                          'content': content})

            # Text is good. Update the content of the action
            action.content = content
            action.save()

            # Closing
            if next_step == 'Save-and-close':
                return redirect('action:index')

    # Return the same form in the same page
    return render(request, 'action/edit_out.html', context=context)
コード例 #4
0
ファイル: views_action.py プロジェクト: radhikavm/ontask_b
def edit_action_in(request, pk):
    """
    View to handle the AJAX form to edit an action in (filter + columns).
    :param request: Request object
    :param pk: Action PK
    :return: HTTP response
    """

    # Check if the workflow is locked
    workflow = get_workflow(request)
    if not workflow:
        return redirect('workflow:index')

    if workflow.nrows == 0:
        messages.error(request,
                       'Workflow has no data. '
                       'Go to Dataops to upload data.')
        return redirect(reverse('action:index'))

    # Get the action and the columns
    try:
        action = Action.objects.filter(
            Q(workflow__user=request.user) |
            Q(workflow__shared=request.user)
        ).distinct().prefetch_related('columns').get(pk=pk)
    except ObjectDoesNotExist:
        return redirect('action:index')

    if action.is_out:
        # Trying to edit an incorrect action. Redirect to index
        return redirect('action:index')

    # See if the action has a filter or not
    try:
        filter_condition = Condition.objects.get(
            action=action, is_filter=True
        )
    except Condition.DoesNotExist:
        filter_condition = None
    except Condition.MultipleObjectsReturned:
        return render(request, 'error.html',
                      {'message': 'Malfunction detected when retrieving filter '
                                  '(action: {0})'.format(action.id)})

    # Get the number of rows in DF selected by filter.
    if filter_condition:
        filter_condition.n_rows_selected = \
            pandas_db.num_rows(action.workflow.id, filter_condition.formula)
        filter_condition.save()

    # Column names suitable to insert
    columns_selected = action.columns.filter(is_key=False).order_by('position')
    columns_to_insert = [c for c in workflow.columns.all()
                         if not c.is_key and c not in columns_selected]

    # Has key column and has no-key column
    has_no_key = action.columns.filter(is_key=False).exists()
    has_empty_description = columns_selected.filter(
        description_text=''
    ).exists()

    # Create the context info.
    ctx = {'action': action,
           'filter_condition': filter_condition,
           'selected_rows':
               filter_condition.n_rows_selected if filter_condition else -1,
           'total_rows': workflow.nrows,
           'query_builder_ops': workflow.get_query_builder_ops_as_str(),
           'has_data': ops.workflow_has_table(action.workflow),
           'key_selected': action.columns.filter(is_key=True).first(),
           'columns_to_insert': columns_to_insert,
           'column_selected_table': ColumnSelectedTable(
               columns_selected.values('id', 'name', 'description_text'),
               orderable=False,
               extra_columns=[
                   ('operations',
                    OperationsColumn(
                        verbose_name='Ops',
                        template_file=ColumnSelectedTable.ops_template,
                        template_context=lambda record: {'id': record['id'],
                                                         'aid': action.id})
                    )]
           ),
           'has_no_key': has_no_key,
           'has_empty_description': has_empty_description}

    return render(request, 'action/edit_in.html', ctx)
コード例 #5
0
def save_condition_form(request, form, template_name, action, condition,
                        is_filter):
    """
    Function to process the AJAX form to create and update conditions and
    filters.
    :param request: HTTP request
    :param form: Form being used to ask for the fields
    :param template_name: Template being used to render the form
    :param action: The action to which the condition is attached to
    :param condition: Condition object being manipulated or None if creating
    :param is_filter: The condition is a filter
    :return:
    """
    # Ajax response
    data = dict()

    # In principle we re-render until proven otherwise
    data['form_is_valid'] = False

    # The condition is new if no value is given
    is_new = condition is None

    if is_new:
        condition_id = -1
    else:
        condition_id = condition.id

    # Context for rendering
    context = {
        'form': form,
        'action_id': action.id,
        'condition_id': condition_id
    }

    # If the method is GET or the form is not valid, re-render the page.
    if request.method == 'GET' or not form.is_valid():

        # If the request has the 'action_content' field, update the action
        action_content = request.GET.get('action_content', None)
        if action_content:
            action.content = action_content
            action.save()

        data['html_form'] = render_to_string(template_name,
                                             context,
                                             request=request)
        return JsonResponse(data)

    if is_filter:
        # Process the filter form
        # If this is a create filter operation, but the action has one,
        # flag the error
        if is_new and Condition.objects.filter(action=action,
                                               is_filter=True).exists():
            # Should not happen. Go back to editing the action
            data['form_is_valid'] = True
            data['html_redirect'] = reverse('action:edit_out',
                                            kwargs={'pk': action.id})
            return JsonResponse(data)

        log_type = 'filter'
    else:
        # Verify that the condition name does not exist yet (Uniqueness FIX)
        qs = Condition.objects.filter(name=form.cleaned_data['name'],
                                      action=action,
                                      is_filter=False)
        if (is_new and qs.exists()) or \
                (not is_new and qs.filter(~Q(id=condition_id)).exists()):
            form.add_error(
                'name',
                'A condition with that name already exists in this action')
            data['html_form'] = render_to_string(template_name,
                                                 context,
                                                 request=request)
            return JsonResponse(data)
        # Verify that the condition name does not collide with column names
        workflow = get_workflow(request, action.workflow.id)
        if not workflow:
            # Workflow is not accessible. Go back to the index.
            data['form_is_valid'] = True
            data['html_redirect'] = reverse('workflow:index')
            return JsonResponse(data)

        # New condition name does not collide with column name
        if form.cleaned_data['name'] in workflow.get_column_names():
            form.add_error('name',
                           'A column name with that name already exists.')
            context = {
                'form': form,
                'action_id': action.id,
                'condition_id': condition_id
            }
            data['html_form'] = render_to_string(template_name,
                                                 context,
                                                 request=request)
            return JsonResponse(data)

        # New condition name does not collide with attribute names
        if form.cleaned_data['name'] in workflow.attributes.keys():
            form.add_error('name',
                           'The workflow has an attribute with this name.')
            context = {
                'form': form,
                'action_id': action.id,
                'condition_id': condition_id
            }
            data['html_form'] = render_to_string(template_name,
                                                 context,
                                                 request=request)
            return JsonResponse(data)

        # If condition name has changed, rename appearances in the content
        # field of the action.
        if form.old_name and 'name' in form.changed_data:
            # Performing string substitution in the content and saving
            replacing = '{{% if {0} %}}'
            action.content = action.content.replace(
                replacing.format(form.old_name),
                replacing.format(condition.name))
            action.save()

        log_type = 'condition'

    # Ok, here we can say that the data in the form is correct.
    data['form_is_valid'] = True

    # Proceed to update the DB
    if is_new:
        # Update the fields not in the form

        # Get the condition from the form, but don't commit as there are
        # changes pending.
        condition = form.save(commit=False)

        condition.action = action
        condition.is_filter = is_filter
        condition.save()
    else:
        condition = form.save()

    if is_filter:
        # Update the number of selected rows applying the new formula
        action.n_selected_rows = \
            pandas_db.num_rows(action.workflow.id, condition.formula)

    # Update the action
    action.save()

    # Log the event
    formula, _ = evaluate_node_sql(condition.formula)
    if is_new:
        log_type += '_create'
    else:
        log_type += '_update'

    # Log the event
    logs.ops.put(
        request.user, log_type, condition.action.workflow, {
            'id': condition.id,
            'name': condition.name,
            'selected_rows': action.n_selected_rows,
            'formula': formula
        })

    data['html_redirect'] = reverse('action:edit_out',
                                    kwargs={'pk': action.id})
    return JsonResponse(data)
コード例 #6
0
def edit_action_in(request, workflow, action):
    """
    View to handle the AJAX form to edit an action in (filter + columns).
    :param request: Request object
    :param workflow: workflow
    :param action: Action
    :return: HTTP response
    """

    # Get filter or None
    filter_condition = action.get_filter()

    # Get the number of rows in DF selected by filter.
    if filter_condition:
        filter_condition.n_rows_selected = \
            pandas_db.num_rows(action.workflow.id, filter_condition.formula)
        filter_condition.save()

    # Column names suitable to insert
    columns_selected = action.columns.filter(is_key=False).order_by('position')
    columns_to_insert = [
        c for c in workflow.columns.all()
        if not c.is_key and c not in columns_selected
    ]

    # Has key column and has no-key column
    has_no_key = action.columns.filter(is_key=False).exists()
    has_empty_description = columns_selected.filter(
        description_text='').exists()

    # Create the context info.
    ctx = {
        'action':
        action,
        'filter_condition':
        filter_condition,
        'selected_rows':
        filter_condition.n_rows_selected if filter_condition else -1,
        'total_rows':
        workflow.nrows,
        'query_builder_ops':
        workflow.get_query_builder_ops_as_str(),
        'has_data':
        ops.workflow_has_table(action.workflow),
        'key_selected':
        action.columns.filter(is_key=True).first(),
        'columns_to_insert':
        columns_to_insert,
        'column_selected_table':
        ColumnSelectedTable(
            columns_selected.values('id', 'name', 'description_text'),
            orderable=False,
            extra_columns=[('operations',
                            OperationsColumn(
                                verbose_name='Ops',
                                template_file=ColumnSelectedTable.ops_template,
                                template_context=lambda record: {
                                    'id': record['id'],
                                    'aid': action.id
                                }))]),
        'has_no_key':
        has_no_key,
        'has_empty_description':
        has_empty_description
    }

    return render(request, 'action/edit_in.html', ctx)