Exemplo n.º 1
0
    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
Exemplo n.º 2
0
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]))
Exemplo n.º 3
0
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