<commit_before># coding: utf-8
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _
from rest_framework import exceptions, viewsets, status, renderers
from rest_framework.decorators import action
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, \
    DestroyModelMixin, ListModelMixin
from rest_framework.response import Response
from rest_framework_extensions.mixins import NestedViewSetMixin

from kpi.constants import (
    CLONE_ARG_NAME,
    PERM_SHARE_ASSET,
    PERM_VIEW_ASSET,
)
from kpi.deployment_backends.kc_access.utils import \
    remove_applicable_kc_permissions
from kpi.models.asset import Asset
from kpi.models.object_permission import ObjectPermission
from kpi.permissions import AssetNestedObjectPermission
from kpi.serializers.v2.asset_permission_assignment import (
    AssetBulkInsertPermissionSerializer,
    AssetPermissionAssignmentSerializer,
)
from kpi.utils.object_permission_helper import ObjectPermissionHelper
from kpi.utils.viewset_mixins import AssetNestedObjectViewsetMixin


class AssetPermissionAssignmentViewSet(AssetNestedObjectViewsetMixin,
                                       NestedViewSetMixin,
                                       CreateModelMixin, RetrieveModelMixin,
                                       DestroyModelMixin, ListModelMixin,
                                       viewsets.GenericViewSet):
    """
    ## Permission assignments of an asset

    This endpoint shows assignments on an asset. An assignment implies:

    - a `Permission` object
    - a `User` object

    **Roles' permissions:**

    - Owner sees all permissions
    - Editors see all permissions
    - Viewers see owner's permissions and their permissions
    - Anonymous users see only owner's permissions


    `uid` - is the unique identifier of a specific asset

    **Retrieve assignments**
    <pre class="prettyprint">
    <b>GET</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/
    </pre>

    > Example
    >
    >       curl -X GET https://[kpi]/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/


    **Assign a permission**
    <pre class="prettyprint">
    <b>POST</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/
    </pre>

    > Example
    >
    >       curl -X POST https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/ \\
    >            -H 'Content-Type: application/json' \\
    >            -d '<payload>'  # Payload is sent as the string


    > _Payload to assign a permission_
    >
    >        {
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{codename}/",
    >        }

    > _Payload to assign partial permissions_
    >
    >        {
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{partial_permission_codename}/",
    >           "partial_permissions": [
    >               {
    >                   "url": "https://[kpi]/api/v2/permissions/{codename}/",
    >                   "filters": [
    >                       {"_submitted_by": {"$in": ["{username}", "{username}"]}}
    >                   ]
    >              },
    >           ]
    >        }

    N.B.:

    - Only submissions support partial (`view`) permissions so far.
    - Filters use Mongo Query Engine to narrow down results.
    - Implied permissions will be also assigned. (e.g. `change_asset` will add `view_asset` too)



    **Remove a permission assignment**

    <pre class="prettyprint">
    <b>DELETE</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/{permission_uid}/
    </pre>

    > Example
    >
    >       curl -X DELETE https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/pG6AeSjCwNtpWazQAX76Ap/


    **Assign all permissions at once**

    <span class='label label-danger'>All permissions will erased (except the owner's) before new assignments</span>
    <pre class="prettyprint">
    <b>POST</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/bulk/
    </pre>

    > Example
    >
    >       curl -X POST https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/bulk/

    > _Payload to assign all permissions at once_
    >
    >        [{
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{codename}/",
    >        },
    >        {
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{codename}/",
    >        },...]


    **Clone permissions from another asset**

    <span class='label label-danger'>All permissions will erased (except the owner's) before new assignments</span>
    <pre class="prettyprint">
    <b>PATCH</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/clone/
    </pre>

    > Example
    >
    >       curl -X PATCH https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/clone/

    > _Payload to clone permissions from another asset_
    >
    >        {
    >           "clone_from": "{source_asset_uid}"
    >        }

    ### CURRENT ENDPOINT
    """

    model = ObjectPermission
    lookup_field = "uid"
    serializer_class = AssetPermissionAssignmentSerializer
    permission_classes = (AssetNestedObjectPermission,)
    pagination_class = None

    @action(detail=False, methods=['POST'], renderer_classes=[renderers.JSONRenderer],
            url_path='bulk')
    def bulk_assignments(self, request, *args, **kwargs):
        """
        Assigns all permissions at once for the same asset.

        :param request:
        :return: JSON
        """

        assignments = request.data

        # We don't want to lock tables, only queries to rollback in case
        # one assignment fails.
        with transaction.atomic():

            # First delete all assignments before assigning new ones.
            # If something fails later, this query should rollback
            perms_to_delete = self.asset.permissions.exclude(
                user__username=self.asset.owner.username)

            # Delete all related KoBoCat permissions as well
            for perm in perms_to_delete.all():
                remove_applicable_kc_permissions(self.asset,
                                                 perm.user,
                                                 perm.permission.codename)
            perms_to_delete.delete()

            for assignment in assignments:
                context_ = dict(self.get_serializer_context())
                if 'partial_permissions' in assignment:<commit_msg>Use `object.remove_perm()` to bulk delete permission assignments prior to assign new ones.<commit_after># coding: utf-8
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _
from rest_framework import exceptions, viewsets, status, renderers
from rest_framework.decorators import action
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, \
    DestroyModelMixin, ListModelMixin
from rest_framework.response import Response
from rest_framework_extensions.mixins import NestedViewSetMixin

from kpi.constants import (
    CLONE_ARG_NAME,
    PERM_SHARE_ASSET,
    PERM_VIEW_ASSET,
)
from kpi.deployment_backends.kc_access.utils import \
    remove_applicable_kc_permissions
from kpi.models.asset import Asset
from kpi.models.object_permission import ObjectPermission
from kpi.permissions import AssetNestedObjectPermission
from kpi.serializers.v2.asset_permission_assignment import (
    AssetBulkInsertPermissionSerializer,
    AssetPermissionAssignmentSerializer,
)
from kpi.utils.object_permission_helper import ObjectPermissionHelper
from kpi.utils.viewset_mixins import AssetNestedObjectViewsetMixin


class AssetPermissionAssignmentViewSet(AssetNestedObjectViewsetMixin,
                                       NestedViewSetMixin,
                                       CreateModelMixin, RetrieveModelMixin,
                                       DestroyModelMixin, ListModelMixin,
                                       viewsets.GenericViewSet):
    """
    ## Permission assignments of an asset

    This endpoint shows assignments on an asset. An assignment implies:

    - a `Permission` object
    - a `User` object

    **Roles' permissions:**

    - Owner sees all permissions
    - Editors see all permissions
    - Viewers see owner's permissions and their permissions
    - Anonymous users see only owner's permissions


    `uid` - is the unique identifier of a specific asset

    **Retrieve assignments**
    <pre class="prettyprint">
    <b>GET</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/
    </pre>

    > Example
    >
    >       curl -X GET https://[kpi]/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/


    **Assign a permission**
    <pre class="prettyprint">
    <b>POST</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/
    </pre>

    > Example
    >
    >       curl -X POST https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/ \\
    >            -H 'Content-Type: application/json' \\
    >            -d '<payload>'  # Payload is sent as the string


    > _Payload to assign a permission_
    >
    >        {
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{codename}/",
    >        }

    > _Payload to assign partial permissions_
    >
    >        {
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{partial_permission_codename}/",
    >           "partial_permissions": [
    >               {
    >                   "url": "https://[kpi]/api/v2/permissions/{codename}/",
    >                   "filters": [
    >                       {"_submitted_by": {"$in": ["{username}", "{username}"]}}
    >                   ]
    >              },
    >           ]
    >        }

    N.B.:

    - Only submissions support partial (`view`) permissions so far.
    - Filters use Mongo Query Engine to narrow down results.
    - Implied permissions will be also assigned. (e.g. `change_asset` will add `view_asset` too)



    **Remove a permission assignment**

    <pre class="prettyprint">
    <b>DELETE</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/{permission_uid}/
    </pre>

    > Example
    >
    >       curl -X DELETE https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/pG6AeSjCwNtpWazQAX76Ap/


    **Assign all permissions at once**

    <span class='label label-danger'>All permissions will erased (except the owner's) before new assignments</span>
    <pre class="prettyprint">
    <b>POST</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/bulk/
    </pre>

    > Example
    >
    >       curl -X POST https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/bulk/

    > _Payload to assign all permissions at once_
    >
    >        [{
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{codename}/",
    >        },
    >        {
    >           "user": "https://[kpi]/api/v2/users/{username}/",
    >           "permission": "https://[kpi]/api/v2/permissions/{codename}/",
    >        },...]


    **Clone permissions from another asset**

    <span class='label label-danger'>All permissions will erased (except the owner's) before new assignments</span>
    <pre class="prettyprint">
    <b>PATCH</b> /api/v2/assets/<code>{uid}</code>/permission-assignments/clone/
    </pre>

    > Example
    >
    >       curl -X PATCH https://[kpi]/api/v2/assets/aSAvYreNzVEkrWg5Gdcvg/permission-assignments/clone/

    > _Payload to clone permissions from another asset_
    >
    >        {
    >           "clone_from": "{source_asset_uid}"
    >        }

    ### CURRENT ENDPOINT
    """

    model = ObjectPermission
    lookup_field = "uid"
    serializer_class = AssetPermissionAssignmentSerializer
    permission_classes = (AssetNestedObjectPermission,)
    pagination_class = None

    @action(detail=False, methods=['POST'], renderer_classes=[renderers.JSONRenderer],
            url_path='bulk')
    def bulk_assignments(self, request, *args, **kwargs):
        """
        Assigns all permissions at once for the same asset.

        :param request:
        :return: JSON
        """

        assignments = request.data

        # We don't want to lock tables, only queries to rollback in case
        # one assignment fails.
        with transaction.atomic():

            # First delete all assignments before assigning new ones.
            # If something fails later, this query should rollback
            perms_to_delete = self.asset.permissions.exclude(
                user__username=self.asset.owner.username)
            for perm in perms_to_delete.all():
                self.asset.remove_perm(perm.user,
                                       perm.permission.codename)

            for assignment in assignments:
                context_ = dict(self.get_serializer_context())
                if 'partial_permissions' in assignment:
{'fraction_non_alphanumeric': 0.11204648737242047, 'fraction_numerical': 0.004618937644341801, 'mean_word_length': 2.3277144273673773, 'pattern_counts': {'":': 28, '<': 73, '<?xml version=': 0, '>': 177, 'https://': 28, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 13, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2001103', 'n_tokens_mistral': 4016, 'n_tokens_neox': 3826, 'n_words': 839}