Example #1
0
    def create(self, validated_data, **kwargs):
        action_obj = Action(
            workflow=self.context['workflow'],
            name=validated_data['name'],
            description_text=validated_data['description_text'],
            is_out=validated_data['is_out'],
            serve_enabled=validated_data['serve_enabled'],
            active_from=validated_data['active_from'],
            active_to=validated_data['active_to'],
            content=validated_data['content'])

        action_obj.save()

        # Load the conditions pointing to the action
        condition_data = ConditionSerializer(data=validated_data.get(
            'conditions', []),
                                             many=True,
                                             context={'action': action_obj})
        if condition_data.is_valid():
            condition_data.save()
        else:
            action_obj.delete()
            return None

        # Update the condition variables for each formula if not present
        for condition in action_obj.conditions.all():
            if condition.columns.all().count() == 0:
                col_names = get_variables(condition.formula)
                # Add the corresponding columns to the condition
                condition.columns.set(self.context['workflow'].columns.filter(
                    name__in=col_names))

        # Load the columns pointing to the action (if any)
        columns = ColumnNameSerializer(
            data=validated_data.get('columns'),
            many=True,
            required=False,
        )
        if columns.is_valid():
            for citem in columns.data:
                column = action_obj.workflow.columns.get(name=citem['name'])
                action_obj.columns.add(column)
            action_obj.save()
        else:
            action_obj.delete()
            return None

        return action_obj
Example #2
0
    def create(self, validated_data, **kwargs):
        condition_obj = None

        try:
            # Bypass create to insert the reference to the action (in context)
            condition_obj = Condition(
                action=self.context['action'],
                name=validated_data['name'],
                description_text=validated_data['description_text'],
                formula=validated_data['formula'],
                n_rows_selected=validated_data.get('n_rows_selected', -1),
                is_filter=validated_data['is_filter'],
            )

            condition_obj.save()

            if validated_data.get('columns'):
                # Load the columns pointing to the action (if any)
                columns = ColumnNameSerializer(
                    data=validated_data.get('columns'),
                    many=True,
                    required=False,
                )
                if columns.is_valid():
                    cnames = [c['name'] for c in columns.data]
                else:
                    raise Exception(_('Incorrect column data'))
            else:
                cnames = get_variables(condition_obj.formula)

            # Set the condition values
            condition_obj.columns.set(
                condition_obj.action.workflow.columns.filter(name__in=cnames)
            )

            # Save condition object
            condition_obj.save()

            if condition_obj.n_rows_selected == -1:
                # Number of rows selected is not up to date, update
                condition_obj.update_n_rows_selected()
        except Exception:
            if condition_obj and condition_obj.id:
                condition_obj.delete()
            raise

        return condition_obj
Example #3
0
    def set_content(self, content):
        """
        Set the action content and update the list of columns
        :return: Update the DB
        """
        # Assign the content and clean the new lines
        self.content = content
        self.clean_new_lines()

        # Update the list of columns used in the action
        cond_names = self.get_action_conditions()
        colnames = list(
            itertools.chain.from_iterable([
                get_variables(x.formula)
                for x in self.conditions.filter(name__in=cond_names)
            ]))
        self.columns.add(
            *[x for x in self.workflow.columns.filter(name__in=colnames)])
def add_columns_to_conditions(apps, schema_editor):
    """
    Traverse all conditions in all actions of all workflows to add the many
    to many relation to the columns.

    :param apps:
    :param schema_editor:
    :return:
    """
    if schema_editor.connection.alias != 'default':
        return

    for workflow in Workflow.objects.all():
        for action in workflow.actions.all():
            for condition in action.conditions.all():
                # Get variable names in the formula
                col_names = get_variables(condition.formula)

                # Add the corresponding columns to the condition
                condition.columns.set(
                    workflow.columns.filter(name__in=col_names))
Example #5
0
    def create(self, validated_data, **kwargs):

        # Process first the used_columns field to get a sense of how many
        # columns, their type how many of them new, etc. etc.
        new_columns = []
        for citem in validated_data['used_columns']:
            cname = citem.get('name', None)
            if not cname:
                raise Exception('Incorrect column name {0}.'.format(cname))
            col = Column.objects.filter(workflow=self.context['workflow'],
                                        name=cname).first()
            if not col:
                # new column
                if citem['is_key']:
                    raise Exception('New action cannot have non-existing key '
                                    'column {0}'.format(cname))

                # Accummulate the new columns just in case we have to undo
                # the changes
                new_columns.append(citem)
                continue

            # existing column
            if col.data_type != citem.get('data_type', None) or \
                    col.is_key != citem['is_key'] or \
                    set(col.categories) != set(citem['categories']):
                # The two columns are different
                raise Exception(
                    'Imported column {0} is different from existing '
                    'one.'.format(cname))
        new_column_names = [x['name'] for x in new_columns]

        action_obj = None
        try:
            # used_columns has been verified.
            action_obj = Action(
                workflow=self.context['workflow'],
                name=validated_data['name'],
                description_text=validated_data['description_text'],
                is_out=validated_data['is_out'],
                serve_enabled=validated_data['serve_enabled'],
                active_from=validated_data['active_from'],
                active_to=validated_data['active_to'],
                content=validated_data.get('content', ''))

            action_obj.save()

            if new_columns:
                # There are some new columns that need to be created
                column_data = ColumnSerializer(data=new_columns,
                                               many=True,
                                               context=self.context)

                # And save its content
                if column_data.is_valid():
                    column_data.save()
                    workflow = self.context['workflow']
                    df = pandas_db.load_from_db(self.context['workflow'].id)
                    if df is None:
                        # If there is no data frame, there is no point on
                        # adding columns.
                        Column.objects.filter(
                            workflow=self.context['workflow'],
                            name__in=new_column_names).delete()
                        action_obj.delete()
                        raise Exception('Action cannot be imported with and '
                                        'empty data table')

                    for col in Column.objects.filter(
                            workflow=workflow, name__in=new_column_names):
                        # Add the column with the initial value
                        df = ops.data_frame_add_column(df, col, None)

                        # Update the column position
                        col.position = len(df.columns)
                        col.save()

                    # Store the df to DB
                    ops.store_dataframe_in_db(df, workflow.id)
                else:
                    raise Exception('Unable to create column data')

            # Load the conditions pointing to the action
            condition_data = ConditionSerializer(
                data=validated_data.get('conditions', []),
                many=True,
                context={'action': action_obj})
            if condition_data.is_valid():
                condition_data.save()
            else:
                raise Exception('Unable to create condition information')

            # Update the condition variables for each formula if not present
            for condition in action_obj.conditions.all():
                if condition.columns.all().count() == 0:
                    col_names = get_variables(condition.formula)
                    # Add the corresponding columns to the condition
                    condition.columns.set(
                        self.context['workflow'].columns.filter(
                            name__in=col_names))

            # Load the columns field
            columns = ColumnNameSerializer(data=validated_data['columns'],
                                           many=True,
                                           required=False,
                                           context=self.context)
            if columns.is_valid():
                for citem in columns.data:
                    column = action_obj.workflow.columns.get(
                        name=citem['name'])
                    action_obj.columns.add(column)
                columns.save()
            else:
                raise Exception('Unable to create columns field')
        except Exception:
            if action_obj and action_obj.id:
                action_obj.delete()
            Column.objects.filter(workflow=self.context['workflow'],
                                  name__in=new_column_names).delete()
            raise

        return action_obj
Example #6
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,
        'add': is_new
    }

    # If the method is GET or the form is not valid, re-render the page.
    if request.method == 'GET' or not form.is_valid():
        data['html_form'] = render_to_string(template_name,
                                             context,
                                             request=request)
        return JsonResponse(data)

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

    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 action.get_filter():
            # Should not happen. Go back to editing the action
            data['form_is_valid'] = True
            data['html_redirect'] = ''
            return JsonResponse(data)
    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,
                'add': is_new
            }
            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 list(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,
                'add': is_new
            }
            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
            # TODO: Review!
            replacing = '{{% if {0} %}}'
            action.content = action.content.replace(
                escape(replacing.format(form.old_name)),
                escape(replacing.format(condition.name)))
            action.save()

    # 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:
        # 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()

    # Update the number of selected rows for the conditions
    condition.update_n_rows_selected()

    # Update the columns field
    condition.columns.set(
        action.workflow.columns.filter(
            name__in=get_variables(condition.formula)))

    # Update the condition
    condition.save()

    # Log the event
    formula, _ = evaluate(condition.formula, NodeEvaluation.EVAL_SQL)
    if is_new:
        if is_filter:
            log_type = Log.FILTER_CREATE
        else:
            log_type = Log.CONDITION_CREATE
    else:
        if is_filter:
            log_type = Log.FILTER_UPDATE
        else:
            log_type = Log.CONDITION_UPDATE

    # Log the event
    Log.objects.register(
        request.user, log_type, condition.action.workflow, {
            'id': condition.id,
            'name': condition.name,
            'selected_rows': condition.n_rows_selected,
            'formula': formula
        })

    data['html_redirect'] = ''
    return JsonResponse(data)