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
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
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))
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
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)