def toggle_delete(request): """ Delete an object. """ type_map = dict(job=Job, data=Data, recipe=Analysis) uid = request.POST.get('uid', "") obj_type = request.POST.get('type', '') obj_model = type_map.get(obj_type) if not obj_model: return ajax_error(f"Invalid data type:{obj_type}") obj = obj_model.objects.filter(uid=uid).first() if not obj: return ajax_error("Object does not exists.") access = auth.is_writable(user=request.user, project=obj.project) # Toggle the delete state if the user has write access if access: auth.delete_object(obj=obj, request=request) # Re-set project counts obj.project.set_counts(save=True) counts = obj_model.objects.filter(project=obj.project, deleted=False).count() return ajax_success(msg='Toggled delete', counts=counts) return ajax_error("Invalid action")
def ajax_move(request): pid = request.POST.get("id", 0) user = request.user project = Project.objects.filter(id=pid).first() # Get the board. board = auth.recent_clipboard(request=request) key, vals = board next_url = auth.resolve_paste_url(key=key, project=project) count = len(vals) if not project: return ajax_error(msg="Project does not exist.") if not auth.is_writable(user=user, project=project): return ajax_error(msg="You do not have access to move here.") # Move objects in clipboard to given project. auth.move(uids=vals, project=project, otype=key, user=user) # Clear the clipboard after moving. auth.clear(request=request) return ajax_success(msg=f"Moved {count} items into project.", redirect=next_url)
def ajax_paste(request): """ Paste the most recent """ pid = request.POST.get("id", 0) user = request.user project = Project.objects.filter(id=pid).first() # Get the board. board = auth.recent_clipboard(request=request) key, vals = board count = len(vals) if not project: return ajax_error(msg="Project does not exist.") if not auth.is_writable(user=user, project=project): return ajax_error(msg="You do not have access to paste here.") if not count: return ajax_error(msg="Clipboard is empty") # The target of this action is to clone. clone = request.POST.get('target') == CLONED_RECIPES # Paste the clipboard item into the project auth.paste(board=board, user=user, project=project, clone=clone) # Resolve the redirect url. next_url = auth.resolve_paste_url(key=key, project=project) # Clear the clipboard after pasting. auth.clear(request=request) return ajax_success(msg=f"Pasted {count} items into project.", redirect=next_url)
def drop(request, klass=None): """ Order objects in a list using drag and drop. 'rank' attribute is required . """ user = request.user # Current object uid being moved/dropped source = int(request.POST.get("source_id", 0)) source = klass.objects.filter(pk=source).first() # The object we intend to move it under top = int(request.POST.get("parent_id", 0)) top = klass.objects.filter(pk=top).first() # Next object after the 'source' bottom = int(request.POST.get("next_id", 0)) bottom = klass.objects.filter(pk=bottom).first() project = source.project # Check if the user has write access to source before moving. if not auth.is_writable(user=user, project=project): ajax_error(msg="You need write access to move objects.") # Compute and update the source with a new rank. maxrank = klass.objects.order_by('-rank').first().rank source.rank = auth.compute_rank(source=source, top=top, bottom=bottom, klass=klass, maxrank=maxrank) klass.objects.filter(pk=source.pk).update(rank=source.rank) return ajax_success(msg="Successfully moved")
def project_view(request, uid, template_name="project_info.html", active='info', show_summary=None, extra_context={}): """ This view handles the project info, data list, recipe list, result list views. """ page = request.GET.get('page') # The user making the request user = request.user # The project that is viewed. project = Project.objects.filter(uid=uid).first() # Select all the data in the project. data_list = project.data_set.filter(deleted=False).order_by("-lastedit_date", "rank", "-date").all() data_paginator = Paginator(data_list, per_page=settings.PER_PAGE) data_list = data_paginator.get_page(page) recipe_list = project.analysis_set.filter(deleted=False).order_by("-rank", "-lastedit_date", "-date").all() # Annotate each recipe with the number of jobs it has. recipe_list = recipe_list.annotate(job_count=Count("job", filter=Q(job__deleted=False))) recipe_paginator = Paginator(recipe_list, per_page=settings.PER_PAGE) recipe_list = recipe_paginator.get_page(page) job_list = project.job_set.filter(deleted=False).order_by("-lastedit_date").all() # Filter job results by analysis filter_uid = request.GET.get('filter', '') recipe_filter = Analysis.objects.filter(uid=filter_uid).first() # The recipe filter exists if recipe_filter: job_list = job_list.filter(analysis=recipe_filter) # Add related content. job_list = job_list.select_related("analysis") job_paginator = Paginator(job_list, per_page=settings.PER_PAGE) job_list = job_paginator.get_page(page) # Who has write access write_access = auth.is_writable(user=user, project=project) # Build the context for the project. context = dict(project=project, data_list=data_list, recipe_list=recipe_list, job_list=job_list, active=active, recipe_filter=recipe_filter, write_access=write_access, rerun_btn=True, include_copy=False) # Compute counts for the project. counts = get_counts(project) # Update conext with the counts. context.update(counts) # Add any extra context that may come from parameters. context.update(extra_context) return render(request, template_name, context)
def recipe_view(request, uid): """ Edit meta-data associated with a recipe. """ # The user making the request. user = request.user # The recipe that needs to be edited. recipe = Analysis.objects.filter(uid=uid).annotate( job_count=Count("job", filter=Q(job__deleted=False))).first() # The project that recipe belongs to. project = recipe.project # Initial form loading via a GET request. form = forms.RecipeForm(instance=recipe, user=request.user, project=project) # Fills in project level counts (results, data and recipe counts). counts = get_counts(project) # Disable buttons if project not writeable. btn_state = '' if auth.is_writable(user=user, project=project) else 'disabled' # Get the list of jobs required to view recipe results jobs = recipe.job_set.filter( deleted=False).order_by("-lastedit_date").all() # Check to see if this recipe is runnable by the user. is_runnable = auth.authorize_run(user=user, recipe=recipe) # Check to see if recipe is editable editable = auth.writeable_recipe(user=user, source=recipe) # Generate the context. context = dict(recipe=recipe, job_list=jobs, project=project, form=form, btn_state=btn_state, is_runnable=is_runnable, activate='Recipe View', rerun_btn=False, include_copy=False, editable=editable) # Update context with counts. context.update(counts) return render(request, 'recipe_view.html', context)
def manage_access(request): access_map = dict(none=Access.NO_ACCESS, read=Access.READ_ACCESS, write=Access.WRITE_ACCESS, share=Access.SHARE_ACCESS) # Get the current user, project and access user_id = request.POST.get('user_id', '') project_uid = request.POST.get('project_uid', '') access_str = request.POST.get('access', '') user = User.objects.filter(id=user_id).first() new_access = access_map.get(access_str) project = Project.objects.filter(uid=project_uid).first() is_writable = auth.is_writable(user=request.user, project=project) # Validate submitted values. if not project: return ajax_error("Project does not exist.") if not user: return ajax_error("User does not exist.") if not new_access: return ajax_error(f"Invalid access option: {access_str}") if user == request.user: return ajax_error("Can not change your own access") if user == project.owner: return ajax_error("Can not change the project owner's access") if not is_writable: return ajax_error("You need write access to manage access.") # Check current user access. access = Access.objects.filter(user=user, project=project).first() # Update existing access if access: Access.objects.filter(id=access.id).update(access=new_access) # Create a new access object else: Access.objects.create(user=user, project=project, access=new_access) no_access = new_access == Access.NO_ACCESS return ajax_success("Changed access.", no_access=no_access)
def project_info(request, uid): user = request.user project = Project.objects.filter(uid=uid).first() # Show counts for the project. counts = get_counts(project) # Who has write access write_access = auth.is_writable(user=user, project=project) if user.is_authenticated: access = Access.objects.filter(user=user, project=project).first() else: access = Access(access=Access.NO_ACCESS) access = access or Access(access=Access.NO_ACCESS) context = dict(project=project, active="info", write_access=write_access, access=access) context.update(counts) return render(request, "project_info.html", context)
def ajax_paste(request): """ Paste the most recent """ pid = request.POST.get("id", 0) user = request.user project = Project.objects.filter(id=pid).first() # Get the board. board = auth.recent_clipboard(request=request) key, vals = board count = len(vals) if not project: return ajax_error(msg="Project does not exist.") if not auth.is_writable(user=user, project=project): return ajax_error(msg="You do not have access to paste here.") if not count: return ajax_error(msg="Clipboard is empty") # The target of this action is to clone. clone = request.POST.get('target') # Paste the clipboard item into the project auth.paste(board=board, user=user, project=project, clone=clone) data_url = reverse("data_list", kwargs=dict(uid=project.uid)) recipes_url = reverse("recipe_list", kwargs=dict(uid=project.uid)) # Resolve the redirect url. redir = recipes_url if key == COPIED_RECIPES else data_url # Clear the clipboard after pasting. auth.clear(request=request) return ajax_success(msg=f"Pasted {count} items into project.", redirect=redir)
def writable(project, user): """ Check if user has write access to the project. """ return auth.is_writable(user=user, project=project)