<commit_before>from modularodm import Q
from rest_framework import generics, permissions as drf_permissions
from rest_framework.exceptions import PermissionDenied, ValidationError, NotFound
from rest_framework.status import HTTP_204_NO_CONTENT
from rest_framework.response import Response

from framework.auth.oauth_scopes import CoreScopes

from api.base import generic_bulk_views as bulk_views
from api.base import permissions as base_permissions
from api.base.filters import ODMFilterMixin, ListFilterMixin
from api.base.views import JSONAPIBaseView
from api.base.parsers import JSONAPIOnetoOneRelationshipParser, JSONAPIOnetoOneRelationshipParserForRegularJSON
from api.base.pagination import CommentPagination
from api.base.utils import get_object_or_error, is_bulk_request, get_user_auth, is_truthy
from api.files.serializers import FileSerializer
from api.comments.serializers import CommentSerializer, CommentCreateSerializer
from api.comments.permissions import CanCommentOrPublic
from api.users.views import UserMixin

from api.nodes.serializers import (
    NodeSerializer,
    NodeLinksSerializer,
    NodeDetailSerializer,
    NodeProviderSerializer,
    NodeContributorsSerializer,
    NodeContributorDetailSerializer,
    NodeDraftRegistrationSerializer,
    NodeInstitutionRelationshipSerializer,
    NodeAlternativeCitationSerializer,
    NodeContributorsCreateSerializer
)
from api.nodes.utils import get_file_object

from api.registrations.serializers import RegistrationSerializer
from api.institutions.serializers import InstitutionSerializer
from api.nodes.permissions import (
    IsAdmin,
    AdminOrPublic,
    ContributorOrPublic,
    ContributorOrPublicForPointers,
    ContributorDetailPermissions,
    ReadOnlyIfRegistration,
    ExcludeRetractions,
)
from api.logs.serializers import NodeLogSerializer

from website.exceptions import NodeStateError
from website.util.permissions import ADMIN
from website.models import Node, Pointer, Comment, NodeLog, Institution, DraftRegistration
from website.files.models import FileNode
from framework.auth.core import User
from api.base.utils import default_node_list_query, default_node_permission_query


class NodeMixin(object):
    """Mixin with convenience methods for retrieving the current node based on the
    current URL. By default, fetches the current node based on the node_id kwarg.
    """

    serializer_class = NodeSerializer
    node_lookup_url_kwarg = 'node_id'

    def get_node(self, check_object_permissions=True):
        node = get_object_or_error(
            Node,
            self.kwargs[self.node_lookup_url_kwarg],
            display_name='node'
        )
        # Nodes that are folders/collections are treated as a separate resource, so if the client
        # requests a collection through a node endpoint, we return a 404
        if node.is_collection or node.is_registration:
            raise NotFound
        # May raise a permission denied
        if check_object_permissions:
            self.check_object_permissions(self.request, node)
        return node

class DraftMixin(object):

    serializer_class = NodeDraftRegistrationSerializer

    def get_draft(self):
        draft = get_object_or_error(DraftRegistration, self.kwargs['draft_id'])

        self.check_object_permissions(self.request, draft)
        return draft


class WaterButlerMixin(object):

    path_lookup_url_kwarg = 'path'
    provider_lookup_url_kwarg = 'provider'

    def get_file_item(self, item):
        attrs = item['attributes']
        file_node = FileNode.resolve_class(
            attrs['provider'],
            FileNode.FOLDER if attrs['kind'] == 'folder'
            else FileNode.FILE
        ).get_or_create(self.get_node(check_object_permissions=False), attrs['path'])

        file_node.update(None, attrs, user=self.request.user)

        self.check_object_permissions(self.request, file_node)

        return file_node

    def fetch_from_waterbutler(self):
        node = self.get_node(check_object_permissions=False)
        path = self.kwargs[self.path_lookup_url_kwarg]
        provider = self.kwargs[self.provider_lookup_url_kwarg]<commit_msg>Add additional checks to get_draft to ensure draft is able to be edited.<commit_after>from modularodm import Q
from rest_framework import generics, permissions as drf_permissions
from rest_framework.exceptions import PermissionDenied, ValidationError, NotFound
from rest_framework.status import HTTP_204_NO_CONTENT
from rest_framework.response import Response

from framework.auth.oauth_scopes import CoreScopes

from api.base import generic_bulk_views as bulk_views
from api.base import permissions as base_permissions
from api.base.filters import ODMFilterMixin, ListFilterMixin
from api.base.views import JSONAPIBaseView
from api.base.parsers import JSONAPIOnetoOneRelationshipParser, JSONAPIOnetoOneRelationshipParserForRegularJSON
from api.base.pagination import CommentPagination
from api.base.utils import get_object_or_error, is_bulk_request, get_user_auth, is_truthy
from api.files.serializers import FileSerializer
from api.comments.serializers import CommentSerializer, CommentCreateSerializer
from api.comments.permissions import CanCommentOrPublic
from api.users.views import UserMixin

from api.nodes.serializers import (
    NodeSerializer,
    NodeLinksSerializer,
    NodeDetailSerializer,
    NodeProviderSerializer,
    NodeContributorsSerializer,
    NodeContributorDetailSerializer,
    NodeDraftRegistrationSerializer,
    NodeInstitutionRelationshipSerializer,
    NodeAlternativeCitationSerializer,
    NodeContributorsCreateSerializer
)
from api.nodes.utils import get_file_object

from api.registrations.serializers import RegistrationSerializer
from api.institutions.serializers import InstitutionSerializer
from api.nodes.permissions import (
    IsAdmin,
    AdminOrPublic,
    ContributorOrPublic,
    ContributorOrPublicForPointers,
    ContributorDetailPermissions,
    ReadOnlyIfRegistration,
    ExcludeRetractions,
)
from api.logs.serializers import NodeLogSerializer

from website.exceptions import NodeStateError
from website.util.permissions import ADMIN
from website.models import Node, Pointer, Comment, NodeLog, Institution, DraftRegistration
from website.files.models import FileNode
from framework.auth.core import User
from api.base.utils import default_node_list_query, default_node_permission_query


class NodeMixin(object):
    """Mixin with convenience methods for retrieving the current node based on the
    current URL. By default, fetches the current node based on the node_id kwarg.
    """

    serializer_class = NodeSerializer
    node_lookup_url_kwarg = 'node_id'

    def get_node(self, check_object_permissions=True):
        node = get_object_or_error(
            Node,
            self.kwargs[self.node_lookup_url_kwarg],
            display_name='node'
        )
        # Nodes that are folders/collections are treated as a separate resource, so if the client
        # requests a collection through a node endpoint, we return a 404
        if node.is_collection or node.is_registration:
            raise NotFound
        # May raise a permission denied
        if check_object_permissions:
            self.check_object_permissions(self.request, node)
        return node


class DraftMixin(object):

    serializer_class = DraftRegistrationSerializer

    def get_draft(self):
        node_id = self.kwargs['node_id']
        draft = get_object_or_error(DraftRegistration, self.kwargs['draft_id'])

        if not draft.branched_from._id == node_id:
            raise ValidationError('This draft registration is not created from the given node.')

        if self.request.method == 'PATCH' or self.request.method == 'PUT':
            registered_and_deleted = draft.registered_node and draft.registered_node.is_deleted

            if draft.registered_node and not registered_and_deleted:
                raise PermissionDenied('This draft has already been registered and cannot be modified.')

            if draft.is_pending_review:
                raise PermissionDenied('This draft is pending review and cannot be modified.')

            if draft.requires_approval and draft.is_approved and (not registered_and_deleted):
                raise PermissionDenied('This draft has already been approved and cannot be modified.')

        self.check_object_permissions(self.request, draft)
        return draft


class WaterButlerMixin(object):

    path_lookup_url_kwarg = 'path'
    provider_lookup_url_kwarg = 'provider'

    def get_file_item(self, item):
        attrs = item['attributes']
        file_node = FileNode.resolve_class(
            attrs['provider'],
            FileNode.FOLDER if attrs['kind'] == 'folder'
            else FileNode.FILE
        ).get_or_create(self.get_node(check_object_permissions=False), attrs['path'])

        file_node.update(None, attrs, user=self.request.user)

        self.check_object_permissions(self.request, file_node)

        return file_node

    def fetch_from_waterbutler(self):
        node = self.get_node(check_object_permissions=False)
        path = self.kwargs[self.path_lookup_url_kwarg]
        provider = self.kwargs[self.provider_lookup_url_kwarg]
{'fraction_non_alphanumeric': 0.056963396144682696, 'fraction_numerical': 0.001299545159194282, 'mean_word_length': 4.159217877094972, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5786903', 'n_tokens_mistral': 2526, 'n_tokens_neox': 2442, 'n_words': 653}