<commit_before>import re
from typing import List

import jsonschema
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import IntegrityError, transaction
from django.db.models.deletion import ProtectedError
from django.http.request import QueryDict
from rest_framework import status

from ..core.config import config
from ..core.models import Tag
from ..utils.auth import has_perm, in_some_groups
from ..utils.autoupdate import inform_changed_data, inform_deleted_data
from ..utils.rest_api import (
    CreateModelMixin,
    DestroyModelMixin,
    GenericViewSet,
    ModelViewSet,
    Response,
    ReturnDict,
    UpdateModelMixin,
    ValidationError,
    detail_route,
    list_route,
)
from .access_permissions import (
    CategoryAccessPermissions,
    MotionAccessPermissions,
    MotionBlockAccessPermissions,
    MotionChangeRecommendationAccessPermissions,
    MotionCommentSectionAccessPermissions,
    StatuteParagraphAccessPermissions,
    WorkflowAccessPermissions,
)
from .exceptions import WorkflowError
from .models import (
    Category,
    Motion,
    MotionBlock,
    MotionChangeRecommendation,
    MotionComment,
    MotionCommentSection,
    MotionPoll,
    State,
    StatuteParagraph,
    Submitter,
    Workflow,
)
from .serializers import MotionPollSerializer, StateSerializer


# Viewsets for the REST API


class MotionViewSet(ModelViewSet):
    """
    API endpoint for motions.

    There are a lot of views. See check_view_permissions().
    """

    access_permissions = MotionAccessPermissions()
    queryset = Motion.objects.all()

    def check_view_permissions(self):
        """
        Returns True if the user has required permissions.
        """
        if self.action in ("list", "retrieve"):
            result = self.get_access_permissions().check_permissions(self.request.user)
        elif self.action in ("metadata", "partial_update", "update", "destroy"):
            result = has_perm(self.request.user, "motions.can_see")
            # For partial_update, update and destroy requests the rest of the check is
            # done in the update method. See below.
        elif self.action in ("create", "set_state"):
            result = has_perm(self.request.user, "motions.can_see")
            # For create the rest of the check is done in the respective method. See below.
        elif self.action in (
            "manage_multiple_state",
            "set_recommendation",
            "manage_multiple_recommendation",
            "follow_recommendation",
            "manage_multiple_submitters",
            "manage_multiple_tags",
            "create_poll",
        ):
            result = has_perm(self.request.user, "motions.can_see") and has_perm(
                self.request.user, "motions.can_manage_metadata"
            )
        elif self.action in ("sort", "manage_comments"):
            result = has_perm(self.request.user, "motions.can_see") and has_perm(
                self.request.user, "motions.can_manage"
            )
        elif self.action == "support":
            result = has_perm(self.request.user, "motions.can_see") and has_perm(
                self.request.user, "motions.can_support"
            )
        else:
            result = False
        return result

    def destroy(self, request, *args, **kwargs):
        """
        Destroy is allowed if the user has manage permissions, or he is the submitter and
        the current state allows to edit the motion.
        """
        motion = self.get_object()

        if not (
            (
                has_perm(request.user, "motions.can_manage")
                or motion.is_submitter(request.user)
                and motion.state.allow_submitter_edit
            )
        ):
            self.permission_denied(request)

        result = super().destroy(request, *args, **kwargs)

        # Fire autoupdate again to save information to OpenSlides history.
        inform_deleted_data(
            [(motion.get_collection_string(), motion.pk)],
            information=["Motion deleted"],
            user_id=request.user.pk,
        )

        return result

    def create(self, request, *args, **kwargs):
        """
        Customized view endpoint to create a new motion.
        """
        # This is a hack to make request.data mutable. Otherwise fields can not be deleted.
        if isinstance(request.data, QueryDict):
            request.data._mutable = True

        # Check if amendment request and if parent motion exists. Check also permissions.
        if request.data.get("parent_id") is not None:
            # Amendment
            if not has_perm(self.request.user, "motions.can_create_amendments"):
                self.permission_denied(request)
            try:
                parent_motion = Motion.objects.get(pk=request.data["parent_id"])
            except Motion.DoesNotExist:
                raise ValidationError({"detail": "The parent motion does not exist."})
        else:
            # Common motion
            if not has_perm(self.request.user, "motions.can_create"):
                self.permission_denied(request)
            parent_motion = None

        # Check permission to send some data.
        if not has_perm(request.user, "motions.can_manage"):
            # Remove fields that the user is not allowed to send.
            # The list() is required because we want to use del inside the loop.
            keys = list(request.data.keys())
            whitelist = ["title", "text", "reason", "category_id"]
            if parent_motion is not None:
                # For creating amendments.
                whitelist.extend(
                    [
                        "parent_id",
                        "amendment_paragraphs",
                        "motion_block_id",  # This and the category_id will be set to the matching
                        # values from parent_motion.
                    ]
                )<commit_msg>fix statute paragraph creation for delegates<commit_after>import re
from typing import List

import jsonschema
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import IntegrityError, transaction
from django.db.models.deletion import ProtectedError
from django.http.request import QueryDict
from rest_framework import status

from ..core.config import config
from ..core.models import Tag
from ..utils.auth import has_perm, in_some_groups
from ..utils.autoupdate import inform_changed_data, inform_deleted_data
from ..utils.rest_api import (
    CreateModelMixin,
    DestroyModelMixin,
    GenericViewSet,
    ModelViewSet,
    Response,
    ReturnDict,
    UpdateModelMixin,
    ValidationError,
    detail_route,
    list_route,
)
from .access_permissions import (
    CategoryAccessPermissions,
    MotionAccessPermissions,
    MotionBlockAccessPermissions,
    MotionChangeRecommendationAccessPermissions,
    MotionCommentSectionAccessPermissions,
    StatuteParagraphAccessPermissions,
    WorkflowAccessPermissions,
)
from .exceptions import WorkflowError
from .models import (
    Category,
    Motion,
    MotionBlock,
    MotionChangeRecommendation,
    MotionComment,
    MotionCommentSection,
    MotionPoll,
    State,
    StatuteParagraph,
    Submitter,
    Workflow,
)
from .serializers import MotionPollSerializer, StateSerializer


# Viewsets for the REST API


class MotionViewSet(ModelViewSet):
    """
    API endpoint for motions.

    There are a lot of views. See check_view_permissions().
    """

    access_permissions = MotionAccessPermissions()
    queryset = Motion.objects.all()

    def check_view_permissions(self):
        """
        Returns True if the user has required permissions.
        """
        if self.action in ("list", "retrieve"):
            result = self.get_access_permissions().check_permissions(self.request.user)
        elif self.action in ("metadata", "partial_update", "update", "destroy"):
            result = has_perm(self.request.user, "motions.can_see")
            # For partial_update, update and destroy requests the rest of the check is
            # done in the update method. See below.
        elif self.action in ("create", "set_state"):
            result = has_perm(self.request.user, "motions.can_see")
            # For create the rest of the check is done in the respective method. See below.
        elif self.action in (
            "manage_multiple_state",
            "set_recommendation",
            "manage_multiple_recommendation",
            "follow_recommendation",
            "manage_multiple_submitters",
            "manage_multiple_tags",
            "create_poll",
        ):
            result = has_perm(self.request.user, "motions.can_see") and has_perm(
                self.request.user, "motions.can_manage_metadata"
            )
        elif self.action in ("sort", "manage_comments"):
            result = has_perm(self.request.user, "motions.can_see") and has_perm(
                self.request.user, "motions.can_manage"
            )
        elif self.action == "support":
            result = has_perm(self.request.user, "motions.can_see") and has_perm(
                self.request.user, "motions.can_support"
            )
        else:
            result = False
        return result

    def destroy(self, request, *args, **kwargs):
        """
        Destroy is allowed if the user has manage permissions, or he is the submitter and
        the current state allows to edit the motion.
        """
        motion = self.get_object()

        if not (
            (
                has_perm(request.user, "motions.can_manage")
                or motion.is_submitter(request.user)
                and motion.state.allow_submitter_edit
            )
        ):
            self.permission_denied(request)

        result = super().destroy(request, *args, **kwargs)

        # Fire autoupdate again to save information to OpenSlides history.
        inform_deleted_data(
            [(motion.get_collection_string(), motion.pk)],
            information=["Motion deleted"],
            user_id=request.user.pk,
        )

        return result

    def create(self, request, *args, **kwargs):
        """
        Customized view endpoint to create a new motion.
        """
        # This is a hack to make request.data mutable. Otherwise fields can not be deleted.
        if isinstance(request.data, QueryDict):
            request.data._mutable = True

        # Check if amendment request and if parent motion exists. Check also permissions.
        if request.data.get("parent_id") is not None:
            # Amendment
            if not has_perm(self.request.user, "motions.can_create_amendments"):
                self.permission_denied(request)
            try:
                parent_motion = Motion.objects.get(pk=request.data["parent_id"])
            except Motion.DoesNotExist:
                raise ValidationError({"detail": "The parent motion does not exist."})
        else:
            # Common motion
            if not has_perm(self.request.user, "motions.can_create"):
                self.permission_denied(request)
            parent_motion = None

        # Check permission to send some data.
        if not has_perm(request.user, "motions.can_manage"):
            # Remove fields that the user is not allowed to send.
            # The list() is required because we want to use del inside the loop.
            keys = list(request.data.keys())
            whitelist = [
                "title",
                "text",
                "reason",
                "category_id",
                "statute_paragraph_id",
            ]
            if parent_motion is not None:
                # For creating amendments.
                whitelist.extend(
                    [
                        "parent_id",
                        "amendment_paragraphs",
                        "motion_block_id",  # This and the category_id will be set to the matching
                        # values from parent_motion.
                    ]
                )
{'fraction_non_alphanumeric': 0.08009787928221859, 'fraction_numerical': 0.0, 'mean_word_length': 2.187158825058487, 'pattern_counts': {'":': 4, '<': 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': '3800168', 'n_tokens_mistral': 3287, 'n_tokens_neox': 3146, 'n_words': 890}