def row_update( request: HttpRequest, workflow: Optional[models.Workflow] = None, ) -> HttpResponse: """Process POST request to update a row in the data table. :param request: Request object with all the data. :param workflow: Workflow being manipulated :return: Http Response with the page rendering. """ # Get the pair key,value to fetch the row from the table update_key = request.GET.get('k') update_val = request.GET.get('v') if not update_key or not update_val: # Malformed request return render(request, 'error.html', {'message': _('Unable to update table row')}) # Get the form populated with the row values form = forms.RowForm(request.POST or None, workflow=workflow, initial_values=sql.get_row( workflow.get_data_frame_table_name(), key_name=update_key, key_value=update_val, column_names=workflow.get_column_names())) if request.method == 'POST' and form.is_valid(): if not form.has_changed(): return redirect('table:display') try: row_values = [ form.cleaned_data[(ONTASK_UPLOAD_FIELD_PREFIX + '%s') % idx] for idx in range(workflow.columns.count()) ] services.update_row_values(workflow, update_key, update_val, row_values) except Exception as exc: form.add_error(None, str(exc)) return render( request, 'dataops/row_filter.html', { 'workflow': workflow, 'form': form, 'cancel_url': reverse('table:display') }) workflow.log(request.user, models.Log.WORKFLOW_DATA_ROW_UPDATE, new_values=list( zip([col.name for col in workflow.columns.all()], [str(rval) for rval in row_values]))) return redirect('table:display') return render(request, 'dataops/row_filter.html', { 'workflow': workflow, 'form': form, 'cancel_url': reverse('table:display') })
def get_row_values( action: models.Action, row_idx: Union[int, Tuple[str, str]], ) -> Dict[str, Union[str, int, float, datetime]]: """Get the values in a row either by index or by key. 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 filter_formula = action.get_filter_formula() # If row_idx is an integer, get the data by index, otherwise, by key if isinstance(row_idx, int): row = pandas.get_table_row_by_index( action.workflow, filter_formula, row_idx, ) else: row = sql.get_row( action.workflow.get_data_frame_table_name(), row_idx[0], row_idx[1], column_names=action.workflow.get_column_names(), filter_formula=filter_formula, ) return row
def test_row_create(self): """Test the view to filter items.""" nrows = self.workflow.nrows # Row create (GET) resp = self.get_response('dataops:rowcreate') self.assertTrue(status.is_success(resp.status_code)) # Row create POST resp = self.get_response('dataops:rowcreate', method='POST', req_params={ '___ontask___upload_0': '8', '___ontask___upload_1': 'text1', '___ontask___upload_2': 'text2', '___ontask___upload_3': '12.1', '___ontask___upload_4': '12.2', '___ontask___upload_5': 'on', '___ontask___upload_6': '', '___ontask___upload_7': '06/07/2019 19:32', '___ontask___upload_8': '06/05/2019 19:23' }) self.assertTrue(status.is_success(resp.status_code)) # Update the workflow self.workflow.refresh_from_db() self.assertEqual(nrows, self.workflow.nrows) # Row create POST resp = self.get_response('dataops:rowcreate', method='POST', req_params={ '___ontask___upload_0': '9', '___ontask___upload_1': 'text add 1', '___ontask___upload_2': 'text add 2', '___ontask___upload_3': '22', '___ontask___upload_4': '23', '___ontask___upload_5': 'on', '___ontask___upload_7': '06/07/2019 19:32', '___ontask___upload_8': '06/05/2019 19:23' }) self.assertEqual(resp.status_code, status.HTTP_302_FOUND) # Update the workflow self.workflow.refresh_from_db() self.assertEqual(nrows + 1, self.workflow.nrows) row_val = get_row(self.workflow.get_data_frame_table_name(), key_name='key', key_value=9) self.assertEqual(row_val['text1'], 'text add 1') self.assertEqual(row_val['text2'], 'text add 2') self.assertEqual(row_val['double1'], 22) self.assertEqual(row_val['double2'], 23)
def test_row_edit(self): """Test the view to filter items.""" nrows = self.workflow.nrows # Row edit (GET) resp = self.get_response('dataops:rowupdate', req_params={ 'k': 'key', 'v': '8' }) self.assertTrue(status.is_success(resp.status_code)) self.assertTrue('Edit learner data' in str(resp.content)) # Get the GET URL with the paramegters request = self.factory.get(reverse('dataops:rowupdate'), { 'k': 'key', 'v': '8' }) request = self.factory.post( request.get_full_path(), { '___ontask___upload_0': '8', '___ontask___upload_1': 'NEW TEXT 1', '___ontask___upload_2': 'NEW TEXT 2', '___ontask___upload_3': '111', '___ontask___upload_4': '222', '___ontask___upload_5': 'on', '___ontask___upload_6': '', '___ontask___upload_7': '06/07/2019 19:32', '___ontask___upload_8': '06/05/2019 19:23' }) request = self.add_middleware(request) resp = row_update(request) self.assertEqual(resp.status_code, status.HTTP_302_FOUND) row_val = get_row(self.workflow.get_data_frame_table_name(), key_name='key', key_value=8) self.assertEqual(row_val['text1'], 'NEW TEXT 1') self.assertEqual(row_val['text2'], 'NEW TEXT 2') self.assertEqual(row_val['double1'], 111) self.assertEqual(row_val['double2'], 222)
def row_update( request: HttpRequest, workflow: Optional[Workflow] = None, ) -> HttpResponse: """Process POST request to update a row in the data table. :param request: Request object with all the data. :return: """ # Get the pair key,value to fetch the row from the table update_key = request.GET.get('k') update_val = request.GET.get('v') if not update_key or not update_val: # Malformed request return render(request, 'error.html', {'message': _('Unable to update table row')}) # Get the form populated with the row values form = RowForm(request.POST or None, workflow=workflow, initial_values=get_row( workflow.get_data_frame_table_name(), key_name=update_key, key_value=update_val, column_names=workflow.get_column_names())) if request.method == 'POST' and form.is_valid(): if not form.has_changed(): return redirect('table:display') # Create the query to update the row column_names = [col.name for col in workflow.columns.all()] row_values = [ form.cleaned_data[(FIELD_PREFIX + '%s') % idx] for idx in range(len(column_names)) ] try: with transaction.atomic(): # Update the row in the db update_row(workflow.get_data_frame_table_name(), column_names, row_values, filter_dict={update_key: update_val}) # verify that the "key" property is maintained in all the # columns. check_key_columns(workflow) except Exception as exc: form.add_error(None, str(exc)) return render( request, 'dataops/row_filter.html', { 'workflow': workflow, 'form': form, 'cancel_url': reverse('table:display') }) # Recompute all the values of the conditions in each of the actions # TODO: Explore how to do this asynchronously (or lazy) map(lambda act: act.update_n_rows_selected(), workflow.actions.all()) # Log the event Log.objects.register( request.user, Log.TABLEROW_UPDATE, workflow, { 'id': workflow.id, 'name': workflow.name, 'new_values': list(zip(column_names, [str(rval) for rval in row_values])) }) return redirect('table:display') return render(request, 'dataops/row_filter.html', { 'workflow': workflow, 'form': form, 'cancel_url': reverse('table:display') })
def get_table_visualization_items( workflow: models.Workflow, rowselect_key: Optional[str] = None, rowselect_val: Optional[Any] = None, pk: Optional[int] = None, ) -> Optional[Tuple[str, Dict]]: """Get a tuple with a template, and a dictionary to visualize a table. :param workflow: Workflow being processed :param rowselect_key: Optional key name to select a row :param rowselect_val: Optional value to select a row :param pk: Primary key of a view (could be none) :return: """ # Get the data frame and the columns df_col_view = _get_df_and_columns(workflow, pk) if not df_col_view: return None df, columns_to_view, view = df_col_view if bool(rowselect_key): template = 'table/stat_row.html' row = sql.get_row( workflow.get_data_frame_table_name(), rowselect_key, rowselect_val, column_names=[col.name for col in columns_to_view], ) else: row = None template = 'table/stat_view.html' vis_scripts = [] visualizations = [] context = { 'style': 'max-width:{0}px; max-height:{1}px; margin: auto;'.format( VISUALIZATION_WIDTH, VISUALIZATION_HEIGHT), } for idx, column in enumerate(columns_to_view): # Add the title and surrounding container visualizations.append( '<hr/><h4 class="text-center">' + column.name + '</h4>') # If all values are empty, no need to proceed if all(not col_data for col_data in df[column.name]): visualizations.append( '<p>' + _('No values in this column') + '</p>') continue if row and not row[column.name]: visualizations.append( '<p class="alert-warning">' + _('No value in this column') + '</p>', ) column_viz = _get_column_visualisations( column, df[[column.name]], vis_scripts=vis_scripts, viz_id='column_{0}'.format(idx), single_val=row[column.name] if row else None, context=context) visualizations.extend([vis.html_content for vis in column_viz]) return template, { 'value': rowselect_val, 'view': view, 'vis_scripts': vis_scripts, 'visualizations': visualizations}