def evaluate_row(action, row_idx): """ Given an action and a row index, evaluate the content of the action for that index. The evaluation depends on the action type. Given an action object and a row index: 1) Access the attached workflow 2) Obtain the row of data from the appropriate data frame 3) Process further depending on the type of action :param action: Action object :param row_idx: Row index to use for evaluation :return HTML content resulting from the evaluation """ # Step 1: Get the workflow to access the data. No need to check for # locking information as it has been checked upstream. workflow = Workflow.objects.get(pk=action.workflow.id) # 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 # If row_idx is an integer, get the data by index, otherwise, by key if isinstance(row_idx, int): row_values = ops.get_table_row_by_index(workflow, cond_filter, row_idx) else: row_values = pandas_db.get_table_row_by_key(workflow, cond_filter, row_idx) if row_values is None: # No rows satisfy the given condition return None # Invoke the appropriate function depending on the action type if action.is_out: return evaluate_row_out(action, row_values) return evaluate_row_in(action, row_values)
def get_row_values(action, row_idx): """ Given an action and a row index, obtain the appropriate row of values from the data frame. :param action: Action object :param row_idx: Row index to use for evaluation :return Dictionary with the data row """ # Step 1: Get the row of data from the DB cond_filter = action.get_filter() # If row_idx is an integer, get the data by index, otherwise, by key if isinstance(row_idx, int): result = ops.get_table_row_by_index(action.workflow, cond_filter, row_idx) else: result = pandas_db.get_table_row_by_key(action.workflow, cond_filter, row_idx) return result
def evaluate_row(action, row_idx): """ Given an action object and a row index: 1) Access the attached workflow 2) Obtain the row of data from the appropriate data frame 3) Evaluate the conditions with respect to the values in the row 4) Create a context with the result of evaluating the conditions, attributes and column names to values 5) Run the template with the context 6) Return the resulting object (HTML?) :param action: Action object with pointers to conditions, filter, workflow, etc. :param row_idx: Either an integer (row index), or a pair key=value to filter :return: None to flag an error """ # Step 1: Get the workflow to access the data. No need to check for # locking information as it has been checked upstream. workflow = Workflow.objects.get(pk=action.workflow.id) # 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 # If row_idx is an integer, get the data by index, otherwise, by key if isinstance(row_idx, int): row_values = ops.get_table_row_by_index(workflow, cond_filter, row_idx) else: row_values = pandas_db.get_table_row_by_key(workflow, cond_filter, row_idx) if row_values is None: # No rows satisfy the given condition return None # Step 3: Evaluate all the conditions condition_eval = {} condition_anomalies = [] for condition in Condition.objects.filter(action__id=action.id).values( 'name', 'is_filter', 'formula'): if condition['is_filter']: # Filter can be skipped in this stage continue # Evaluate the condition try: condition_eval[condition['name']] = \ dataops.formula_evaluation.evaluate_top_node( condition['formula'], row_values ) except OntaskException as e: condition_anomalies.append(e.value) # If any of the variables was incorrectly evaluated, we replace the # content and replace it by something noting this anomaly if condition_anomalies: return render_to_string('action/incorrect_preview.html', {'missing_values': condition_anomalies}) # 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 # First create the template with the string stored in the action try: result = render_template(action.content, context, action) except TemplateSyntaxError as e: return render_to_string('action/syntax_error.html', {'msg': e.message}) # Render the text return result
def serve_action_in(request, action, user_attribute_name, is_inst): """ Function that given a request, and an action IN, it performs the lookup and data input of values. :param request: HTTP request :param action: Action In :param user_attribute_name: The column name used to check for email :param is_inst: Boolean stating if the user is instructor :return: """ # Get the attribute value if is_inst: user_attribute_value = request.GET.get('uatv', None) else: user_attribute_value = request.user.email # Get the active columns attached to the action columns = [c for c in action.columns.all() if c.is_active] #print( "user_attribute_name" + user_attribute_name ) #print(action.workflow) #print(user_attribute_name) #print(user_attribute_value) #print( [c.name for c in columns] ) # Get the row values. User_instance has the record used for verification row_pairs = pandas_db.get_table_row_by_key( action.workflow, None, (user_attribute_name, user_attribute_value), [c.name for c in columns]) #print( row_pairs ) #print(columns) # If the data has not been found, flag if not row_pairs: if not is_inst: return render(request, '404.html', {}) messages.error(request, 'Data not found in the table') return redirect(reverse('action:run', kwargs={'pk': action.id})) # Bind the form with the existing data form = EnterActionIn(request.POST or None, columns=columns, values=row_pairs.values(), show_key=is_inst) cancel_url = None if is_inst: cancel_url = reverse('action:run', kwargs={'pk': action.id}) # Create the context context = {'form': form, 'action': action, 'cancel_url': cancel_url} if request.method == 'GET' or not form.is_valid(): return render(request, 'action/run_row.html', context) # Correct POST request! if not form.has_changed(): if not is_inst: return redirect(reverse('action:thanks')) return redirect(reverse('action:run', kwargs={'pk': action.id})) # Post with different data. # Update content in the DB set_fields = [] set_values = [] where_field = None where_value = None log_payload = [] # Create the SET name = value part of the query #print(columns) for idx, column in enumerate(columns): try: #print(field_prefix + '%s' % idx) value = form.cleaned_data[field_prefix + '%s' % idx] #print( " value:" + value ) if column.is_key: #print( column ) #print( " is key" ) if not where_field: # Remember one unique key for selecting the row where_field = column.name where_value = value continue set_fields.append(column.name) set_values.append(value) log_payload.append((column.name, value)) except: pass ##Wen patch for where_field and where_vlue is None## if not where_field: where_field = user_attribute_name if not where_value: where_value = user_attribute_value pandas_db.update_row(action.workflow.id, set_fields, set_values, [where_field], [where_value]) # Recompute all the values of the conditions in each of the actions for act in action.workflow.actions.all(): act.update_n_rows_selected() # Log the event logs.ops.put( request.user, 'tablerow_update', action.workflow, { 'id': action.workflow.id, 'name': action.workflow.name, 'new_values': log_payload }) # If not instructor, just thank the user! if not is_inst: return render(request, 'thanks.html', {}) # Back to running the action return redirect(reverse('action:run', kwargs={'pk': action.id}))
def serve_action_in(request, action, user_attribute_name, is_inst): """ Function that given a request, and an action IN, it performs the lookup and data input of values. :param request: HTTP request :param action: Action In :param user_attribute_name: The column name used to check for email :param is_inst: Boolean stating if the user is instructor :return: """ # Get the attribute value if is_inst: user_attribute_value = request.GET.get('uatv', None) else: user_attribute_value = request.user.email # Get the active columns attached to the action columns = [c for c in action.columns.all() if c.is_active] if action.shuffle: # Shuffle the columns if needed random.seed(request.user) random.shuffle(columns) # Get the row values. User_instance has the record used for verification row_pairs = pandas_db.get_table_row_by_key( action.workflow, None, (user_attribute_name, user_attribute_value), [c.name for c in columns]) # If the data has not been found, flag if not row_pairs: if not is_inst: return render(request, '404.html', {}) messages.error(request, _('Data not found in the table')) return redirect(reverse('action:run', kwargs={'pk': action.id})) # Bind the form with the existing data form = EnterActionIn(request.POST or None, columns=columns, values=list(row_pairs.values()), show_key=is_inst) cancel_url = None if is_inst: cancel_url = reverse('action:run', kwargs={'pk': action.id}) # Create the context context = {'form': form, 'action': action, 'cancel_url': cancel_url} if request.method == 'GET' or not form.is_valid() or \ request.POST.get('lti_version', None): return render(request, 'action/run_survey_row.html', context) # Post with different data. # Update content in the DB set_fields = [] set_values = [] where_field = 'email' where_value = request.user.email log_payload = [] # Create the SET name = value part of the query for idx, column in enumerate(columns): if not is_inst and column.is_key: # If it is a learner request and a key column, skip continue value = form.cleaned_data[field_prefix + '%s' % idx] if column.is_key: # Remember one unique key for selecting the row where_field = column.name where_value = value continue set_fields.append(column.name) set_values.append(value) log_payload.append((column.name, value)) pandas_db.update_row(action.workflow.id, set_fields, set_values, [where_field], [where_value]) # Recompute all the values of the conditions in each of the actions for act in action.workflow.actions.all(): act.update_n_rows_selected() # Log the event and update its content in the action log_item = Log.objects.register( request.user, Log.TABLEROW_UPDATE, action.workflow, { 'id': action.workflow.id, 'name': action.workflow.name, 'new_values': log_payload }) # Modify the time of execution for the action action.last_executed_log = log_item action.save() # If not instructor, just thank the user! if not is_inst: return render(request, 'thanks.html', {}) # Back to running the action return redirect(reverse('action:run', kwargs={'pk': action.id}))