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'])
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()