def action_out_save_content(request, pk): """ :param request: HTTP request (POST) :param pk: Action ID :return: Nothing, changes reflected in the DB """ # Try to get the workflow first workflow = get_workflow(request) if not workflow: return JsonResponse({}) # Get the action try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user)).distinct().get(pk=pk) except ObjectDoesNotExist: return JsonResponse({}) # Wrong type of action. if not action.is_out: return JsonResponse({}) # If the request has the 'action_content', update the action action_content = request.POST.get('action_content', None) if action_content: action.content = action_content action.save() return JsonResponse({})
def show_ss(request): # Try to get workflow and if not present, go to home page workflow = get_workflow(request) if not workflow: return JsonResponse({'error': 'Incorrect request. Unable to process'}) # Check that the GET parameter are correctly given try: draw = int(request.POST.get('draw', None)) start = int(request.POST.get('start', None)) length = int(request.POST.get('length', None)) except ValueError: return JsonResponse({'error': 'Incorrect request. Unable to process'}) # Get the column information from the request and the rest of values. search_value = request.POST.get('search[value]', None) # Get the logs qs = Log.objects.filter(workflow__id=workflow.id) recordsTotal = qs.count() if search_value: # Refine the log qs = qs.filter( Q(user__email__icontains=search_value) | Q(name__icontains=search_value) | Q(payload__icontains=search_value), workflow__id=workflow.id, ).distinct() # Order and select values qs = qs.order_by(F('created').desc()).values_list('id', 'created', 'user__email', 'name') recordsFiltered = qs.count() final_qs = [] for item in qs[start:start + length]: row = [ item[1].astimezone(pytz.timezone(ontask_settings.TIME_ZONE)), item[2], item[3], """<button type="submit" class="btn btn-primary btn-sm js-log-view" data-url="{0}" data-toggle="tooltip" title="View the content of this log"> <span class="glyphicon glyphicon-eye-open"></span> View </button> """.format(reverse('logs:view', kwargs={'pk': item[0]})) ] # Add the row to the final query_set final_qs.append(row) # Result to return as AJAX response data = { 'draw': draw, 'recordsTotal': recordsTotal, 'recordsFiltered': recordsFiltered, 'data': final_qs } # Render the page with the table return JsonResponse(data)
def action_index(request): """ Render the list of actions attached to a workflow. :param request: Request object :return: HTTP response with the table. """ # Get the appropriate workflow object workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Get the actions actions = Action.objects.filter( workflow__id=workflow.id).values('id', 'name', 'description_text', 'is_out', 'modified', 'serve_enabled') # Context to render the template context = {} # Build the table only if there is anything to show (prevent empty table) if actions.count() > 0: context['table'] = ActionTable(actions, orderable=False) return render(request, 'action/index.html', context)
def shuffle_questions(request, pk): """ Operation to drop a column from action in :param request: Request object :param pk: Action PK :return: HTML response """ # Check if the workflow is locked workflow = get_workflow(request) if not workflow: return reverse('workflow:index') if workflow.nrows == 0: messages.error( request, _('Workflow has no data. Go to Dataops to upload data.')) return redirect(reverse('action:index')) # Get the action and the columns try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user)).distinct().prefetch_related( 'columns').get(pk=pk) except ObjectDoesNotExist: return redirect(reverse('action:index')) action.shuffle = not action.shuffle action.save() return JsonResponse({'shuffle': action.shuffle})
def view_index(request): """ Render the list of views attached to a workflow :param request: :return: HTTP response with the table """ # Get the appropriate workflow object workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Get the views views = workflow.views.values('id', 'name', 'description_text', 'modified') # Context to render the template context = { 'query_builder_ops': workflow.get_query_builder_ops_as_str() } # Build the table only if there is anything to show (prevent empty table) context['table'] = ViewTable(views, orderable=False) return render(request, 'table/view_index.html', context)
def view_add(request): """ Create a new view by processing the GET/POST requests related to the form. :param request: Request object :return: AJAX response """ # Get the workflow element workflow = get_workflow(request) if not workflow: return JsonResponse( {'form_is_valid': True, 'html_redirect': reverse('workflow:index')} ) if workflow.nrows == 0: messages.error( request, _('Cannot add a view to a workflow without data')) return JsonResponse( {'form_is_valid': True, 'html_redirect': ''} ) # Form to read/process data form = ViewAddForm(request.POST or None, workflow=workflow) return save_view_form(request, form, 'table/includes/partial_view_add.html')
def view(request, pk): # Ajax response data = dict() data['form_is_valid'] = False # Try to get workflow and if not present, go to home page workflow = get_workflow(request) if not workflow: data['form_is_valid'] = True data['html_redirect'] = reverse('workflow:index') return JsonResponse(data) # Get the log item log_item = Log.objects.filter(pk=pk, user=request.user, workflow=workflow).first() # If the log item is not there, flag! if not log_item: messages.error(request, _('Incorrect log number requested')) return redirect(reverse('logs:index')) context = {'log_item': log_item} context['json_pretty'] = json.dumps(log_item.payload, sort_keys=True, indent=4) return render(request, 'logs/view.html', context)
def display_view_ss(request, pk): """ AJAX function to provide a subset of the table for visualisation. The subset is defined by the elements in a view :param request: HTTP request from dataTables :param pk: Primary key of the view to be used :return: AJAX response """ workflow = get_workflow(request) if not workflow: return JsonResponse( {'error': _('Incorrect request. Unable to process')}) # If there is not DF, go to workflow details. if not ops.workflow_id_has_table(workflow.id): return JsonResponse({'error': _('There is no data in the table')}) try: view = View.objects.get(pk=pk, workflow=workflow) except ObjectDoesNotExist: # The view has not been found, so it must be due to a session expire return JsonResponse({'error': _('Incorrect view reference')}) return render_table_display_data(request, workflow, view.columns.all(), view.formula, view.id)
def get_workflow_action(request, pk): """ Function that returns the action for the given PK and the workflow for the session. :param request: :param pk: Action id. :return: (workflow, Action) or None """ # Get the workflow first workflow = get_workflow(request) if not workflow: return None if workflow.nrows == 0: messages.error( request, 'Workflow has no data. ' 'Go to "Manage table data" to upload data.') return None # Get the action try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user)).distinct().get(pk=pk) except ObjectDoesNotExist: return None return workflow, action
def email_create(request, pk): """ Request data to send emails. Form asking for subject line, email column, etc. :param request: HTTP request (GET) :param pk: Action key :return: """ # Get the action attached to this request try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user)).distinct().get(pk=pk) except ObjectDoesNotExist: return redirect('workflow:index') workflow = get_workflow(request, action.workflow.id) if not workflow: return redirect('workflow:index') # See if this action already has a scheduled action schedule_item = None qs = ScheduledEmailAction.objects.filter( action=action, type='email_send', status=0, # Pending deleted=False) if qs: if settings.DEBUG: assert len(qs) == 1 # There should only be one schedule_item = qs[0] return save_email_schedule(request, workflow, action, schedule_item)
def action_index(request): """ Render the list of actions attached to a workflow. :param request: Request object :return: HTTP response with the table. """ # Get the appropriate workflow object workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Get the actions actions = Action.objects.filter( workflow__id=workflow.id) # Context to render the template context = {'has_table': ops.workflow_has_table(workflow)} # Build the table only if there is anything to show (prevent empty table) qset = [] for action in actions: qset.append({'id': action.id, 'name': action.name, 'description_text': action.description_text, 'is_out': action.is_out, 'is_correct': action.is_correct, 'modified': action.modified, 'serve_enabled': action.serve_enabled}) context['table'] = ActionTable(qset, orderable=False) return render(request, 'action/index.html', context)
def edit_email(request, pk): """ Edit an existing scheduled email action :param request: HTTP request :param pk: primary key of the email action :return: HTTP response """ # Get first the current workflow workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Get the scheduled action from the parameter in the URL try: s_item = ScheduledEmailAction.objects.filter( action__workflow=workflow, type='email_send', deleted=False, ).get(pk=pk) except ObjectDoesNotExist: return redirect('workflow:index') # Get the action field. action = s_item.action return save_email_schedule(request, workflow, action, s_item)
def uploadmerge(request): # Get the workflow that is being used workflow = get_workflow(request) if not workflow: return redirect('workflow:index') return render(request, 'dataops/uploadmerge.html', {'nrows': workflow.nrows})
def edit_description(request, pk): """ Edit the description attached to an action :param request: AJAX request :param pk: Action ID :return: AJAX response """ # Try to get the workflow first workflow = get_workflow(request) if not workflow: return JsonResponse({'form_is_valid': True, 'html_redirect': reverse('workflow:index')}) # Get the action try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user)).distinct().get(pk=pk) except ObjectDoesNotExist: return JsonResponse({'form_is_valid': True, 'html_redirect': reverse('action:index')}) # Initial result. In principle, re-render page data = {'form_is_valid': False} # Create the form form = ActionDescriptionForm(request.POST or None, instance=action) # Process the POST if request.method == 'POST' and form.is_valid(): # Save item in the DB action.save() # Log the event logs.ops.put(request.user, 'action_update', action.workflow, {'id': action.id, 'name': action.name, 'workflow_id': workflow.id, 'workflow_name': workflow.name}) # Request is correct data['form_is_valid'] = True data['html_redirect'] = '' # Enough said. Respond. return JsonResponse(data) data['html_form'] = render_to_string( 'action/includes/partial_action_edit_description.html', {'form': form, 'action': action}, request=request) return JsonResponse(data)
def render_last_exec(self, record): workflow = get_workflow(self.request) log_item = workflow.logs.filter(user=self.request.user, name=Log.PLUGIN_EXECUTE, payload__name=record.name).order_by( F('created').desc()).first() if not log_item: return '--' return log_item.created
def row_delete(request): """ Handle the steps to delete a row in the table :param request: HTTP request :return: AJAX response """ # We only accept ajax requests here if not request.is_ajax(): return redirect('workflow:index') # Result to return data = {} # Get the workflow workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Get the key/value pair to delete key = request.GET.get('key', None) value = request.GET.get('value', None) # Process the confirmed response if request.method == 'POST': # The response will require going to the table display anyway data['form_is_valid'] = True data['html_redirect'] = reverse('table:display') # if there is no key or value, flag the message and return to table # view if not key or not value: messages.error(request, _('Incorrect URL invoked to delete a row')) return JsonResponse(data) # Proceed to delete the row pandas_db.delete_table_row_by_key(workflow.id, (key, value)) # Update rowcount workflow.nrows -= 1 workflow.save() # Update the value of all the conditions in the actions for action in workflow.actions.all(): action.update_n_rows_selected() return JsonResponse(data) # Render the page data['html_form'] = render_to_string( 'table/includes/partial_row_delete.html', {'delete_key': '?key={0}&value={1}'.format(key, value)}, request=request ) return JsonResponse(data)
def dataops(request): # Get the workflow that is being used workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Make sure there is no upload table in the db for this workflow if ops.workflow_has_upload_table(workflow): pandas_db.delete_upload_table(workflow.id) return render(request, 'dataops/data_ops.html', {})
def clone(request, pk): """ JSON request to clone a condition. The post request must come with the action_content :param request: Request object :param pk: id of the condition to clone :return: JSON response """ # Check if the workflow is locked workflow = get_workflow(request) if not workflow: return JsonResponse({'html_redirect': reverse('workflow:index')}) # Get the condition condition = Condition.objects.filter(pk=pk).filter( Q(action__workflow__user=request.user) | Q(action__workflow__shared=request.user), is_filter=False).first() if not condition: messages.error(request, _('Condition cannot be cloned.')) return JsonResponse({'html_redirect': reverse('action:index')}) # If the request has the 'action_content', update the action action_content = request.POST.get('action_content', None) if action_content: condition.action.set_content(action_content) condition.action.save() # Get the new name appending as many times as needed the 'Copy of ' new_name = 'Copy of ' + condition.name while condition.action.conditions.filter(name=new_name).exists(): new_name = 'Copy of ' + new_name old_id = condition.id old_name = condition.name condition = ops.clone_condition(condition, new_action=None, new_name=new_name) # Log event Log.objects.register( request.user, Log.CONDITION_CLONE, condition.action.workflow, { 'id_old': old_id, 'id_new': condition.id, 'name_old': old_name, 'name_new': condition.name }) messages.success(request, _('Action successfully cloned.')) # Refresh the page to show the column in the list. return JsonResponse({'html_redirect': ''})
def transform(request): # Get the workflow that is being used workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Traverse the plugin folder and refresh the db content. refresh_plugin_data(request, workflow) table = PluginRegistryTable(PluginRegistry.objects.all(), orderable=False) return render(request, 'dataops/transform.html', {'table': table})
def display(request): """ Initial page to show the table :param request: HTTP request :return: Initial rendering of the page with the table skeleton """ workflow = get_workflow(request) if not workflow: return redirect('workflow:index') return render_table_display_page(request, workflow, None, workflow.columns.all(), reverse('table:display_ss'))
def action_index(request): """ Render the list of actions attached to a workflow. :param request: Request object :return: HTTP response with the table. """ # Get the appropriate workflow object workflow = get_workflow(request) if not workflow: return redirect('workflow:index') return action_index_set(request)
def csvdownload(request, pk=None): """ :param request: HTML request :param pk: If given, the PK of the view to subset the table :return: Return a CSV download of the data in the table """ # Get the appropriate workflow object workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Check if dataframe is present if not ops.workflow_id_has_table(workflow.id): # Go back to show the workflow detail return redirect(reverse('workflow:detail', kwargs={'pk': workflow.id})) # Get the columns from the view (if given) view = None if pk: try: view = View.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user) ).distinct().prefetch_related('columns').get(pk=pk) except ObjectDoesNotExist: # Go back to show the workflow detail return redirect(reverse('workflow:detail', kwargs={'pk': workflow.id})) # Fetch the data frame if view: col_names = [x.name for x in view.columns.all()] else: col_names = workflow.get_column_names() data_frame = pandas_db.get_subframe(workflow.id, view, col_names) # Create the response object response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = \ 'attachment; filename="ontask_table.csv"' # Dump the data frame as the content of the response object data_frame.to_csv(path_or_buf=response, sep=str(','), index=False, encoding='utf-8') return response
def show(request): # Try to get workflow and if not present, go to home page workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Create the context with the column names context = { 'workflow': workflow, 'column_names': ['Date/Time', 'User', 'Event type', 'View'] } # Render the page with the table return render(request, 'logs/show.html', context)
def select_column_action(request, apk, cpk, key=None): """ Operation to add a column to action in :param request: Request object :param apk: Action PK :param cpk: column PK :param key: The columns is a key column :return: JSON response """ # Check if the workflow is locked workflow = get_workflow(request) if not workflow: return reverse('workflow:index') if workflow.nrows == 0: messages.error( request, _('Workflow has no data. Go to "Manage table data" to upload data.' )) return JsonResponse({'html_redirect': reverse('action:index')}) # Get the action and the columns try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user)).distinct().prefetch_related( 'columns').get(pk=apk) except ObjectDoesNotExist: return JsonResponse({'html_redirect': reverse('action:index')}) # Get the column try: column = action.workflow.columns.get(pk=cpk) except ObjectDoesNotExist: return JsonResponse({'html_redirect': reverse('action:index')}) # Parameters are correct, so add the column to the action. if key: current_key = action.columns.filter(is_key=True).first() if current_key: # Remove the existing one action.columns.remove(current_key) if column.is_key: action.columns.add(column) return JsonResponse({'html_redirect': ''}) action.columns.add(column) # Refresh the page to show the column in the list. return JsonResponse({'html_redirect': ''})
def edit_action_in(request, pk): """ View to handle the AJAX form to edit an action in (filter + columns). :param request: Request object :param pk: Action PK :return: JSON response """ # Check if the workflow is locked workflow = get_workflow(request) if not workflow: return redirect('workflow:index') if workflow.nrows == 0: messages.error(request, 'Workflow has no data. ' 'Go to Dataops to upload data.') return redirect(reverse('action:index')) # Get the action and the columns try: action = Action.objects.filter( Q(workflow__user=request.user) | Q(workflow__shared=request.user) ).distinct().prefetch_related('columns').get(pk=pk) except ObjectDoesNotExist: return redirect('action:index') # Create the form form = EditActionInForm(data=request.POST or None, workflow=workflow, instance=action) # Create the context info. ctx = {'action': action, 'query_builder_ops': workflow.get_query_builder_ops_as_str(), 'form': form, } # If it is a GET, or an invalid POST, render the template again if request.method == 'GET' or not form.is_valid(): return render(request, 'action/edit_in.html', ctx) # Valid POST request # Save the element and populate the right columns form.save() # Finish processing return redirect(reverse('action:index'))
def vis_html_content(context, column_name, vtype=None): # Get the workflow workflow = get_workflow(context['request']) if not workflow: raise Exception('Workflow object not found in request') # Check if the column is correct if not Column.objects.filter(workflow=workflow, name=column_name).exists(): raise Exception('Column {0} does not exist'.format(column_name)) if vtype not in [None, 'histogram', 'boxplot']: raise Exception('Visualization type must be histogram or boxplot') # TO BE DESIGNED HOW THE MACRO IS THEN EVALUATED WITH THE RIGHT PERSONAL # DATA. NO IDEA HOW TO DO IT SO FAR. return format_html('<p>{0} {1}</p>'.format(column_name, vtype))
def display_ss(request): """ AJAX function to provide a subset of the table for visualisation :param request: HTTP request from dataTables :return: AJAX response """ workflow = get_workflow(request) if not workflow: return JsonResponse({'error': 'Incorrect request. Unable to process'}) # If there is not DF, go to workflow details. if not ops.workflow_id_has_table(workflow.id): return JsonResponse({'error': 'There is no data in the table'}) return render_table_display_data(request, workflow, workflow.columns.all(), None)
def action_index(request, wid=None): """ Render the list of actions attached to a workflow. :param request: Request object :return: HTTP response with the table. """ # Get the appropriate workflow object workflow = get_workflow(request, wid=wid) if not workflow: return redirect('workflow:index') # Reset object to carry action info throughout dialogs request.session[session_dictionary_name] = {} request.session.save() # Get the actions actions = Action.objects.filter(workflow__id=workflow.id) # Context to render the template context = { 'has_table': ops.workflow_has_table(workflow), 'workflow': workflow } # Build the table only if there is anything to show (prevent empty table) qset = [] for action in actions: qset.append({ 'id': action.id, 'name': action.name, 'description_text': action.description_text, 'action_type': action.get_action_type_display(), 'action_tval': action.action_type, 'is_out': action.is_out, 'is_executable': action.is_executable, 'last_executed_log': action.last_executed_log, 'serve_enabled': action.serve_enabled, 'modified': action.modified }) context['table'] = \ ActionTable(qset, orderable=False) context['no_actions'] = len(qset) == 0 context['qset'] = qset return render(request, 'action/md_index.html', context)
def action_import(request): """ This request imports one action given in a gz file :param request: Http request :return: HTTP response """ # Get workflow workflow = get_workflow(request) if not workflow: return redirect('workflow:index') form = ActionImportForm(request.POST or None, request.FILES or None) context = {'form': form} # If a get request or the form is not valid, render the page. if request.method == 'GET' or not form.is_valid(): return render(request, 'action/import.html', context) new_action_name = form.cleaned_data['name'] if Action.objects.filter( workflow=workflow, workflow__user=request.user, name=new_action_name).exists(): # There is an action with this name. Return error. form.add_error(None, 'An action with this name already exists') return render(request, 'action/import.html', context) # Process the reception of the file if not form.is_multipart(): form.add_error(None, 'Incorrect form request (it is not multipart)') return render(request, 'action/import.html', context) # UPLOAD THE FILE! status = do_import_action(request.user, workflow, form.cleaned_data['name'], request.FILES['file']) # If something went wrong, show at to the top of the page if status: messages.error(request, status) # Go back to the list of actions return redirect('action:index')
def display(request): # Try to get workflow and if not present, go to home page workflow = get_workflow(request) if not workflow: return redirect('workflow:index') # Create the context with the column names context = { 'workflow': workflow, 'column_names': [_('ID'), _('Date/Time'), _('User'), _('Event type')] } # Render the page with the table return render(request, 'logs/display.html', context)