def clean(self): data = super(ZipActionForm, self).clean() # Participant column must be unique pcolumn = data['participant_column'] ufname_column = data['user_fname_column'] # The given column must have unique values if not is_column_table_unique(self.action.workflow.pk, pcolumn): self.add_error( 'participant_column', _('Column needs to have all unique values (no empty cells)') ) return data # If both values are given and they are identical, return with error if pcolumn and ufname_column and pcolumn == ufname_column: self.add_error( None, _('The two columns must be different') ) return data # If a moodle zip has been requested if data.get('zip_for_moodle', False): if not pcolumn or not ufname_column: self.add_error( None, _('A Moodle ZIP requires two column names') ) return data # Participant columns must match the pattern 'Participant [0-9]+' pcolumn_data = get_table_data(self.action.workflow.pk, None, column_names=[pcolumn]) if next((x for x in pcolumn_data if not participant_re.search(str(x[0]))), None): self.add_error( 'participant_column', _('Values in column must have format "Participant [number]"') ) return data
def get_table_row_by_index(workflow, cond_filter, idx): """ Select the set of elements in the row with the given index :param workflow: Workflow object storing the data :param cond_filter: Condition object to filter the data (or None) :param idx: Row number to get (first row is idx = 1) :return: A dictionary with the (column_name, value) data or None if the index is out of bounds """ # Get the data data = get_table_data(workflow.id, cond_filter) # If the data is not there, return None if idx > len(data): return None return dict(zip(workflow.get_column_names(), data[idx - 1]))
def evaluate_action(action, extra_string, column_name): """ Given an action object and an optional string: 1) Access the attached workflow 2) Obtain the data from the appropriate data frame 3) Loop over each data row and 3.1) Evaluate the conditions with respect to the values in the row 3.2) Create a context with the result of evaluating the conditions, attributes and column names to values 3.3) Run the template with the context 3.4) Run the optional string argument with the template and the context 3.5) Select the optional column_name 6) Return the resulting objects: List of (HTMLs body, extra string, column name value) or an error message :param action: Action object with pointers to conditions, filter, workflow, etc. :param extra_string: An extra string to process (something like the email subject line) with the same dictionary as the text in the action. :param column_name: Column from where to extract the special value ( typically the email address) and include it in the result. :return: list of lists resulting from the evaluation of the action """ # Step 1: Get the workflow to access the data and prepare data workflow = Workflow.objects.get(pk=action.workflow.id) col_names = workflow.get_column_names() col_idx = -1 if column_name and column_name in col_names: col_idx = col_names.index(column_name) # Step 2: Get the row of data from the DB try: cond_filter = Condition.objects.get(action__id=action.id, is_filter=True) except ObjectDoesNotExist: cond_filter = None # Step 3: Get the table data result = [] data_table = pandas_db.get_table_data(workflow.id, cond_filter) # Check if the values in the email column are correct emails try: correct_emails = all([validate_email(x[col_idx]) for x in data_table]) if not correct_emails: # column has incorrect email addresses return 'The column with email addresses has incorrect values.' except TypeError: return 'The column with email addresses has incorrect values' for row in data_table: # Get the dict(col_name, value) row_values = dict(zip(col_names, row)) # Step 3: Evaluate all the conditions condition_eval = {} for condition in Condition.objects.filter(action__id=action.id).values( 'is_filter', 'formula', 'name'): if condition['is_filter']: # Filter can be skipped in this stage continue # Evaluate the condition condition_eval[condition['name']] = \ dataops.formula_evaluation.evaluate_top_node( condition['formula'], row_values ) # Step 4: Create the context with the attributes, the evaluation of the # conditions and the values of the columns. attributes = workflow.attributes context = dict(dict(row_values, **condition_eval), **attributes) # Step 5: run the template with the given context # Render the text and append to result try: partial_result = [render_template(action.content, context)] except Exception as e: return 'Syntax error detected in the action text. ' + e.message # If there is extra message, render with context and create tuple if extra_string: try: partial_result.append(render_template(extra_string, context)) except Exception as e: return 'Syntax error detected in the subject. ' + e.message # If column_name was given (and it exists), create a tuple with that # element as the third component if col_idx != -1: partial_result.append(row_values[col_names[col_idx]]) # Append result result.append(partial_result) return result