class WorkflowExportSerializer(serializers.ModelSerializer): """ This serializer is use to export Workflows selecting a subset of actions. Since the SerializerMethodField used for the selection is a read_only field, the import is managed by a different serializer that uses a regular one for the action field (see WorkflowImportSerializer) """ actions = serializers.SerializerMethodField('get_filtered_actions') data_frame = DataFramePandasField( required=False, help_text=_('This field must be the Base64 encoded ' 'result of pandas.to_pickle() function')) columns = ColumnSerializer(many=True, required=False) views = ViewSerializer(many=True, required=False) version = serializers.CharField(read_only=True, default='NO VERSION', allow_blank=True, label="OnTask Version", help_text=_("To guarantee compability")) def get_filtered_actions(self, workflow): # Get the subset of actions specified in the context action_list = self.context.get('selected_actions', []) if not action_list: # No action needs to be included, no need to call the action # serializer return [] # Execute the query set query_set = workflow.actions.filter(id__in=action_list) # Serialize the content and return data serializer = ActionSerializer(instance=query_set, many=True, required=False) return serializer.data def create(self, validated_data, **kwargs): # Initial values workflow_obj = None try: workflow_obj = Workflow( user=self.context['user'], name=self.context['name'], description_text=validated_data['description_text'], nrows=0, ncols=0, attributes=validated_data['attributes'], query_builder_ops=validated_data.get('query_builder_ops', {})) workflow_obj.save() # Create the columns column_data = ColumnSerializer(data=validated_data.get( 'columns', []), many=True, context={'workflow': workflow_obj}) # And save its content if column_data.is_valid(): column_data.save() else: raise Exception(_('Unable to save column information')) # If there is any column with position = 0, recompute (this is to # guarantee backward compatibility. if workflow_obj.columns.filter(position=0).exists(): for idx, c in enumerate(workflow_obj.columns.all()): c.position = idx + 1 c.save() # Load the data frame data_frame = validated_data.get('data_frame', None) if data_frame is not None: ops.store_dataframe_in_db(data_frame, workflow_obj.id, reset_keys=False) # Reconcile now the information in workflow and columns with the # one loaded workflow_obj.data_frame_table_name = \ pandas_db.create_table_name(workflow_obj.pk) workflow_obj.ncols = validated_data['ncols'] workflow_obj.nrows = validated_data['nrows'] workflow_obj.save() # Create the actions pointing to the workflow action_data = ActionSerializer(data=validated_data.get( 'actions', []), many=True, context={'workflow': workflow_obj}) if action_data.is_valid(): action_data.save() else: raise Exception(_('Unable to save column information')) # Create the views pointing to the workflow view_data = ViewSerializer(data=validated_data.get('views', []), many=True, context={'workflow': workflow_obj}) if view_data.is_valid(): view_data.save() else: raise Exception(_('Unable to save column information')) except Exception: # Get rid of the objects created if workflow_obj: if workflow_obj.has_data_frame(): pandas_db.delete_table(workflow_obj.id) if workflow_obj.id: workflow_obj.delete() raise return workflow_obj class Meta(object): model = Workflow # fields = ('description_text', 'nrows', 'ncols', 'attributes', # 'query_builder_ops', 'columns', 'data_frame', 'actions') exclude = ('id', 'user', 'created', 'modified', 'data_frame_table_name', 'session_key', 'shared')
def create(self, validated_data, **kwargs): # Initial values workflow_obj = None try: workflow_obj = Workflow( user=self.context['user'], name=self.context['name'], description_text=validated_data['description_text'], nrows=0, ncols=0, attributes=validated_data['attributes'], query_builder_ops=validated_data.get('query_builder_ops', {})) workflow_obj.save() # Create the columns column_data = ColumnSerializer(data=validated_data.get( 'columns', []), many=True, context={'workflow': workflow_obj}) # And save its content if column_data.is_valid(): column_data.save() else: raise Exception(_('Unable to save column information')) # If there is any column with position = 0, recompute (this is to # guarantee backward compatibility. if workflow_obj.columns.filter(position=0).exists(): for idx, c in enumerate(workflow_obj.columns.all()): c.position = idx + 1 c.save() # Load the data frame data_frame = validated_data.get('data_frame', None) if data_frame is not None: ops.store_dataframe_in_db(data_frame, workflow_obj.id, reset_keys=False) # Reconcile now the information in workflow and columns with the # one loaded workflow_obj.data_frame_table_name = \ pandas_db.create_table_name(workflow_obj.pk) workflow_obj.ncols = validated_data['ncols'] workflow_obj.nrows = validated_data['nrows'] workflow_obj.save() # Create the actions pointing to the workflow action_data = ActionSerializer(data=validated_data.get( 'actions', []), many=True, context={'workflow': workflow_obj}) if action_data.is_valid(): action_data.save() else: raise Exception(_('Unable to save column information')) # Create the views pointing to the workflow view_data = ViewSerializer(data=validated_data.get('views', []), many=True, context={'workflow': workflow_obj}) if view_data.is_valid(): view_data.save() else: raise Exception(_('Unable to save column information')) except Exception: # Get rid of the objects created if workflow_obj: if workflow_obj.has_data_frame(): pandas_db.delete_table(workflow_obj.id) if workflow_obj.id: workflow_obj.delete() raise return workflow_obj
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)) # Update the column categories (just in case the new one has a # different order) col.set_categories(citem['categories']) new_column_names = [x['name'] for x in new_columns] action_type = validated_data.get('action_type', None) if not action_type: if validated_data.get('is_out'): action_type = Action.PERSONALIZED_TEXT else: action_type = Action.SURVEY 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'], action_type=action_type, serve_enabled=validated_data['serve_enabled'], active_from=validated_data['active_from'], active_to=validated_data['active_to'], content=validated_data.get('content', ''), target_url=validated_data.get('target_url', None), shuffle=validated_data.get('shuffle', False)) 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