Ejemplo n.º 1
0
def test_project_role_and_above():
    assert ProjectRole.and_above(ProjectRole.AUTHOR) == [
        ProjectRole.AUTHOR,
        ProjectRole.MANAGER,
        ProjectRole.OWNER,
    ]
    assert ProjectRole.and_above(ProjectRole.MANAGER) == [
        ProjectRole.MANAGER,
        ProjectRole.OWNER,
    ]
    assert ProjectRole.and_above(ProjectRole.OWNER) == [
        ProjectRole.OWNER,
    ]
Ejemplo n.º 2
0
Archivo: nodes.py Proyecto: jlbrewe/hub
    def create(self, request: Request) -> Response:
        """
        Create a node.

        Receives a request with the `node` (as JSON) and possibly other information
        e.g. the `project` the node is associated with, the `app` it was

        Returns the URL of the node.
        """
        serializer = NodeCreateRequest(data=request.data)
        serializer.is_valid(raise_exception=True)

        project = serializer.validated_data.get("project")
        app = serializer.validated_data.get("app", "api")
        host = serializer.validated_data.get("host")
        node = serializer.validated_data.get("node")

        # Check that the user has EDIT permissions for the project,
        # if provided.
        if project:
            try:
                get_projects(request.user).get(
                    id=project.id,
                    role__in=[
                        role.name
                        for role in ProjectRole.and_above(ProjectRole.AUTHOR)
                    ],
                )
            except Project.DoesNotExist:
                raise PermissionDenied

        # Create the node
        try:
            node = Node.objects.create(creator=request.user,
                                       project=project,
                                       app=app,
                                       host=host,
                                       json=node)
        except IntegrityError:
            raise ValidationError(
                dict(key="Attempting to create a duplicate key"))

        serializer = NodeCreateResponse(node, context={"request": request})
        return Response(
            serializer.data,
            status=status.HTTP_201_CREATED,
            # Most of the time this action will requested with `Accept: application/json`.
            # However, in case it is not, `template_name` is required.
            template_name="projects/nodes/retrieve.html",
        )
Ejemplo n.º 3
0
    def get_project(self) -> Project:
        """
        Get the project, checking that user has necessary role for the current action.

        Allow everyone to list and retrieve reviews.
        Only allows EDITOR and above to create or update review objects.
        """
        if not hasattr(self, "project"):
            self.project = get_project(
                self.kwargs,
                self.request.user,
                ProjectRole.and_above(ProjectRole.EDITOR) if self.action
                in ["create", "partial_update", "extract"] else None,
            )
        return self.project
Ejemplo n.º 4
0
    def get_queryset(self):
        """
        Get a list of reviews for a project.

        Only list reviews that are EXTRACTED (ie. complete) unless the user is
        a project EDITOR or above.
        """
        project = self.get_project()
        return (Review.objects.filter(
            Q(status=ReviewStatus.EXTRACTED.name) if project.role not in [
                role.name for role in ProjectRole.and_above(ProjectRole.EDITOR)
            ] else Q(),
            project=project,
        ).order_by("-created").select_related(
            "reviewer", "reviewer__personal_account",
            "review").prefetch_related("review__dois"))
Ejemplo n.º 5
0
 def get_response_context(self, *args, **kwargs):
     """
     Get the context for rendering templates for this view set.
     """
     project = self.get_project()
     instance = kwargs.get("instance")
     is_editor = (project.role in [
         role.name for role in ProjectRole.and_above(ProjectRole.EDITOR)
     ] if instance else None)
     is_reviewer = ((self.request.user == instance.reviewer
                     or self.request.GET.get("key") == instance.key)
                    if instance else None)
     return super().get_response_context(*args,
                                         **kwargs,
                                         account=project.account,
                                         project=project,
                                         is_editor=is_editor,
                                         is_reviewer=is_reviewer)
Ejemplo n.º 6
0
    def get_queryset(self):
        """
        Get the set of projects that the user has access to and which meet filter criteria.

        Does not return temporary projects.

        TODO: Currently this ignores an authenticated user's access to
              projects inherited from membership of a team.
        """
        queryset = get_projects(self.request.user).select_related("account")

        account = self.request.GET.get("account")
        if account:
            queryset = queryset.filter(account_id=account)

        role = self.request.GET.get("role")
        if self.request.user.is_authenticated and role:
            roles = re.split(r"\s*,\s*", role)
            q = Q()
            for part in roles:
                match = re.match(r"([a-zA-Z]+)(\+)?", part)
                if match:
                    role_name, and_above = match.groups()
                    if role_name.lower() == "member":
                        q |= Q(role__isnull=False)
                    else:
                        try:
                            project_role = ProjectRole.from_string(role_name)
                        except ValueError as exc:
                            raise exceptions.ValidationError({"role": str(exc)})
                        else:
                            if and_above:
                                q |= Q(
                                    role__in=[
                                        role.name
                                        for role in ProjectRole.and_above(project_role)
                                    ]
                                )
                            else:
                                q |= Q(role=project_role.name)
                else:
                    raise exceptions.ValidationError(
                        {"role": "Invalid role specification {}".format(part)}
                    )
            queryset = queryset.filter(q)

        public = self.request.GET.get("public")
        if public:
            if public.lower() in ["false", "no", "0"]:
                queryset = queryset.filter(public=False)
            else:
                queryset = queryset.filter(public=True)

        source = self.request.GET.get("source")
        if source:
            try:
                query = Source.query_from_address(source, prefix="sources")
            except ValueError as exc:
                raise exceptions.ValidationError({"source": str(exc)})
            else:
                queryset = queryset.filter(query)

        search = self.request.GET.get("search")
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search)
                | Q(title__icontains=search)
                | Q(description__icontains=search)
            )

        # Ordering favoring those that the user has a role
        # on, has an image set, has a description set, etc
        return (
            queryset.filter(temporary=False)
            .annotate(
                role_rank=Case(
                    When(role=ProjectRole.OWNER.name, then=Value(6)),
                    When(role=ProjectRole.MANAGER.name, then=Value(5)),
                    When(role=ProjectRole.AUTHOR.name, then=Value(4)),
                    When(role=ProjectRole.EDITOR.name, then=Value(3)),
                    When(role=ProjectRole.REVIEWER.name, then=Value(2)),
                    When(role=ProjectRole.READER.name, then=Value(1)),
                    When(role__isnull=True, then=Value(0)),
                    output_field=IntegerField(),
                )
                if self.request.user.is_authenticated
                else Value(0, output_field=IntegerField()),
                has_image=Case(
                    When(image_file__isnull=False, then=Value(True)),
                    default=Value(False),
                    output_field=BooleanField(),
                ),
                # Use regex filter here to exclude nulls, blanks and very short strings
                has_title=Case(
                    When(title__regex=r"^.{1,}$", then=Value(True)),
                    default=Value(False),
                    output_field=BooleanField(),
                ),
                has_description=Case(
                    When(description__regex=r"^.{1,}$", then=Value(True)),
                    default=Value(False),
                    output_field=BooleanField(),
                ),
            )
            .order_by(
                "-featured",
                "-has_image",
                "-has_title",
                "-has_description",
                "-role_rank",
                "-created",
            )
        )
Ejemplo n.º 7
0
    def update(
        self,
        status: str,
        response_message: Optional[str] = None,
        cancel_message: Optional[str] = None,
        user: Optional[User] = None,
        filters: Dict = {},
    ):
        """
        Update the status of a review.

        Checks that the status update makes logical sense and
        records the message and user (if any). Note that a status
        update to `ACCEPTED`, `DECLINED` or `COMPLETED` can be made
        by an anonymous users (a reviewer who has the review key but
        is not an authenticated user).
        """
        if (
            status == ReviewStatus.CANCELLED.name
            and self.status == ReviewStatus.REQUESTED.name
        ):
            self.cancel_message = cancel_message or None
        elif (
            status == ReviewStatus.ACCEPTED.name
            and self.status == ReviewStatus.REQUESTED.name
        ):
            self.reviewer = user
            self.response_message = response_message or None

            # Add user as a REVIEWER to the project (if necessary)
            try:
                agent = ProjectAgent.objects.get(project_id=self.project, user=user)
            except ProjectAgent.DoesNotExist:
                ProjectAgent.objects.create(
                    project_id=self.project, user=user, role=ProjectRole.REVIEWER.name,
                )
            else:
                if agent.role not in ProjectRole.and_above(ProjectRole.REVIEWER):
                    agent.role = ProjectRole.REVIEWER.name
                    agent.save()
        elif (
            status == ReviewStatus.DECLINED.name
            and self.status == ReviewStatus.REQUESTED.name
        ):
            self.reviewer = user
            self.response_message = response_message or None
        elif status == ReviewStatus.COMPLETED.name and self.status in (
            ReviewStatus.PENDING.name,
            ReviewStatus.ACCEPTED.name,
            ReviewStatus.FAILED.name,
        ):
            return self.extract(user, filters)
        elif (
            status == ReviewStatus.REGISTERED.name
            and self.status == ReviewStatus.EXTRACTED.name
        ):
            return self.register(user)
        else:
            raise ValueError(
                f"Review can not be updated from {self.status} to {status}."
            )

        self.status = status
        self.save()
Ejemplo n.º 8
0
def test_project_role_from_string():
    assert ProjectRole.from_string("author") == ProjectRole.AUTHOR
    assert ProjectRole.from_string("AUTHOR") == ProjectRole.AUTHOR
    assert ProjectRole.from_string("AutHOr") == ProjectRole.AUTHOR
    with pytest.raises(ValueError, match='No project role matching "foo"'):
        ProjectRole.from_string("foo")