Beispiel #1
0
def store_workflow_table(
    workflow,
    update_info: Optional[Dict] = None,
):
    """Make a temporary DB table the workflow table.

    It is assumed that there is a temporal table already in the database. The
    function performs the following steps:

    Step 1: Drop the columns that are not being uploaded

    Step 2: Rename the columns (if needed)

    Step 3: Create the workflow columns

    Step 4: Rename the table (temporary to final)

    Step 5: Update workflow fields and update

    :param workflow: Workflow object being manipulated.
    :param update_info: Dictionary with the following fields:
        - initial_column_names: list of column names detected in read phase.
        - rename_column_names: List of new names for the columns
        - column_types: List of types detected after storing in DB
        - keep_key_column: List of booleans to flag if key property is kept
        - columns_to_upload: List of booleans to flag column upload
        The first field is mandatory. The have default values if not provided.
    :return: Nothing. Anomalies are raised as Exceptions
    """
    # Check information on update_info and complete if needed
    if not update_info.get('initial_column_names'):
        raise _('Internal error while processing database.')
    if not update_info.get('rename_column_names'):
        update_info['rename_column_names'] = update_info[
            'initial_column_names']
    if not update_info.get('column_types'):
        raise _('Internal error while processing database.')
    if not update_info.get('keep_key_column'):
        raise _('Internal error while processing database.')
    if not update_info.get('columns_to_upload'):
        update_info['columns_to_upload'] = [True] * len(
            update_info['initial_column_names'])

    db_table = workflow.get_upload_table_name()
    new_columns = []
    for old_n, new_n, data_type, is_key, upload in zip(
            update_info['initial_column_names'],
            update_info['rename_column_names'],
            update_info['column_types'],
            update_info['keep_key_column'],
            update_info['columns_to_upload'],
    ):
        # Detect if the column is new or already exists
        current_col = workflow.columns.filter(name=old_n).first()

        # Step 1: Check if column needs to be uploaded
        if not upload:
            # Column is dropped
            sql.df_drop_column(db_table, old_n)

            if current_col:
                # Dropping an existing column. Incorrect.
                raise _('Invalid column drop operation.')
            continue

        # Step 2: Check if the column must be renamed
        if old_n != new_n:
            # Rename column from old_n to new_n
            sql.db_rename_column(db_table, old_n, new_n)

            if current_col:
                rename_df_column(workflow, old_n, new_n)

        if current_col:
            if current_col.data_type != data_type:
                # If the column type in the DB is different from the one in the
                # object, update
                current_col.data_type = data_type
                current_col.save()
        else:
            # Step 3: Create the column
            new_columns.append((new_n, data_type, is_key))

    # Create the columns
    workflow.add_columns(new_columns)
    workflow.refresh_from_db()

    # Step 4: Rename the table (Drop the original one first
    if workflow.has_table():
        sql.delete_table(workflow.get_data_frame_table_name())
    sql.rename_table(db_table, workflow.get_data_frame_table_name())

    # Step 5: Update workflow fields and save
    workflow.nrows = sql.get_num_rows(workflow.get_data_frame_table_name())
    workflow.set_query_builder_ops()
    workflow.save(update_fields=['nrows', 'query_builder_ops'])
Beispiel #2
0
def delete_column(
    user: get_user_model(),
    workflow: models.Workflow,
    column: models.Column,
    cond_to_delete: Optional[List[models.Condition]] = None,
):
    """Remove column from ontask.workflow.

    Given a workflow and a column, removes it from the workflow (and the
    corresponding data frame

    :param user: User performing the operation
    :param workflow: Workflow object
    :param column: Column object to delete
    :param cond_to_delete: List of conditions to delete after removing the
    column
    :return: Nothing. Effect reflected in the database
    """
    column.log(user, models.Log.COLUMN_DELETE)

    # Drop the column from the DB table storing the data frame
    sql.df_drop_column(workflow.get_data_frame_table_name(), column.name)

    # Reposition the columns above the one being deleted
    workflow.reposition_columns(column.position, workflow.ncols + 1)

    # Delete the column
    column.delete()

    # Update the information in the workflow
    workflow.ncols = workflow.ncols - 1
    workflow.save()

    if not cond_to_delete:
        # The conditions to delete are not given, so calculate them
        # Get the conditions/actions attached to this workflow
        cond_to_delete = [
            cond for cond in models.Condition.objects.filter(
                action__workflow=workflow) if column in cond.columns.all()
        ]

    # If a column disappears, the conditions that contain that variable
    # are removed
    actions_without_filters = []
    for condition in cond_to_delete:
        if condition.is_filter:
            actions_without_filters.append(condition.action)

        # Formula has the name of the deleted column. Delete it
        condition.delete()

    # Traverse the actions for which the filter has been deleted and reassess
    #  all their conditions
    # TODO: Explore how to do this asynchronously (or lazy)
    for act in actions_without_filters:
        act.update_n_rows_selected()

    # If a column disappears, the views that contain only that column need to
    # disappear as well as they are no longer relevant.
    for view in workflow.views.all():
        if view.columns.count() == 0:
            view.delete()