<commit_before>from datetime import datetime
import re

from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.template.defaultfilters import timesince
from djblets.siteconfig.models import SiteConfiguration
from djblets.webapi.decorators import webapi_login_required, \
                                      webapi_permission_required
from djblets.webapi.errors import DOES_NOT_EXIST, INVALID_ATTRIBUTE, \
                                  INVALID_FORM_DATA, PERMISSION_DENIED
from djblets.webapi.resources import WebAPIResource as DjbletsWebAPIResource, \
                                     UserResource as DjbletsUserResource

from reviewboard import get_version_string, get_package_version, is_release
from reviewboard.accounts.models import Profile
from reviewboard.reviews.forms import UploadDiffForm, UploadScreenshotForm
from reviewboard.reviews.models import Comment, DiffSet, FileDiff, Group, \
                                       Repository, ReviewRequest, \
                                       ReviewRequestDraft, Review, \
                                       ScreenshotComment, Screenshot
from reviewboard.scmtools.errors import ChangeNumberInUseError, \
                                        EmptyChangeSetError, \
                                        FileNotFoundError, \
                                        InvalidChangeNumberError
from reviewboard.webapi.decorators import webapi_check_login_required
from reviewboard.webapi.errors import INVALID_REPOSITORY, MISSING_REPOSITORY, \
                                      REPO_FILE_NOT_FOUND


class WebAPIResource(DjbletsWebAPIResource):
    @webapi_check_login_required
    def get(self, request, *args, **kwargs):
        return super(WebAPIResource, self).get(request, *args, **kwargs)

    @webapi_check_login_required
    def get_list(self, request, *args, **kwargs):
        if not self.model:
            return HttpResponseNotAllowed(self.allowed_methods)

        if request.GET.get('counts-only', False):
            result = {
                'count': self.get_queryset(request, is_list=True,
                                           *args, **kwargs).count()
            }

            # Several old API functions had a field name other than 'count'.
            # This is deprecated, but must be kept for backwards-compatibility.
            field_alias = \
                request.GET.get('_count-field-alias',
                                kwargs.get('_count-field-alias', None))

            if field_alias:
                result[field_alias] = result['count']

            return 200, result
        else:
            result = super(WebAPIResource, self).get_list(request,
                                                          *args, **kwargs)

            if request.GET.get('_first-result-only', False):
                # This is intended ONLY for deprecated functions. It is not
                # supported as a valid parameter for APIs and should be ignored.
                # It may return broken results if used outside of its built-in
                # intended uses.
                result = (result[0], {
                    self.name: result[1][self.name_plural][0],
                })

            return result


class BaseCommentResource(WebAPIResource):
    model = Comment
    name = 'diff-comment'
    mutable_fields = ('first_line', 'num_lines', 'text')
    fields = mutable_fields + (
        'id', 'filediff', 'interfilediff', 'timestamp',
        'timesince', 'public', 'user',
    )

    uri_object_key = 'comment_id'

    allowed_methods = ('GET',)

    def get_queryset(self, request, review_request_id, *args, **kwargs):
        return self.model.objects.filter(
            Q(review__public=True) | Q(review__user=request.user),
            filediff__diffset__history__review_request=review_request_id,
            interfilediff__isnull=True)

    def serialize_public_field(self, obj):
        return obj.review.get().public

    def serialize_timesince_field(self, obj):
        return timesince(obj.timestamp)

    def serialize_user_field(self, obj):
        return obj.review.get().user


class FileDiffCommentResource(BaseCommentResource):
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')

    def get_queryset(self, request, review_request_id, diff_revision,
                     *args, **kwargs):
        return super(FileDiffCommentResource, self).get_queryset(
            request, review_request_id, *args, **kwargs).filter(
                filediff__diffset__revision=diff_revision)

    def get_href_parent_ids(self, comment, *args, **kwargs):
        filediff = comment.filediff
        diffset = filediff.diffset
        review_request = diffset.history.review_request.get()

        return {
            'review_request_id': review_request.id,
            'diff_revision': diffset.revision,
            'filediff_id': filediff.id,
        }

    @webapi_login_required
    def create(self, request, review_request_id, diff_revision, filediff_id,
               *args, **kwargs):
        try:
            review_request = ReviewRequest.objects.get(pk=review_request_id)
            filediff = FileDiff.objects.get(
                pk=filediff_id,
                diffset__revision=diff_revision,
                diffset__history__review_request=review_request)
        except (ReviewRequest.DoesNotExist, FileDiff.DoesNotExist):
            return DOES_NOT_EXIST

        self._scan_deprecated_fields(request, **kwargs)

        invalid_fields = {}

        for field_name in request.POST:<commit_msg>Begin to use ObjectDoesNotExist for simplicity, and add WebAPIResponse.verify_fields.<commit_after>from datetime import datetime
import re

from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.db.models import Q
from django.template.defaultfilters import timesince
from djblets.siteconfig.models import SiteConfiguration
from djblets.webapi.decorators import webapi_login_required, \
                                      webapi_permission_required
from djblets.webapi.errors import DOES_NOT_EXIST, INVALID_ATTRIBUTE, \
                                  INVALID_FORM_DATA, PERMISSION_DENIED
from djblets.webapi.resources import WebAPIResource as DjbletsWebAPIResource, \
                                     UserResource as DjbletsUserResource

from reviewboard import get_version_string, get_package_version, is_release
from reviewboard.accounts.models import Profile
from reviewboard.reviews.forms import UploadDiffForm, UploadScreenshotForm
from reviewboard.reviews.models import Comment, DiffSet, FileDiff, Group, \
                                       Repository, ReviewRequest, \
                                       ReviewRequestDraft, Review, \
                                       ScreenshotComment, Screenshot
from reviewboard.scmtools.errors import ChangeNumberInUseError, \
                                        EmptyChangeSetError, \
                                        FileNotFoundError, \
                                        InvalidChangeNumberError
from reviewboard.webapi.decorators import webapi_check_login_required
from reviewboard.webapi.errors import INVALID_REPOSITORY, MISSING_REPOSITORY, \
                                      REPO_FILE_NOT_FOUND


class WebAPIResource(DjbletsWebAPIResource):
    @webapi_check_login_required
    def get(self, request, *args, **kwargs):
        return super(WebAPIResource, self).get(request, *args, **kwargs)

    @webapi_check_login_required
    def get_list(self, request, *args, **kwargs):
        if not self.model:
            return HttpResponseNotAllowed(self.allowed_methods)

        if request.GET.get('counts-only', False):
            result = {
                'count': self.get_queryset(request, is_list=True,
                                           *args, **kwargs).count()
            }

            # Several old API functions had a field name other than 'count'.
            # This is deprecated, but must be kept for backwards-compatibility.
            field_alias = \
                request.GET.get('_count-field-alias',
                                kwargs.get('_count-field-alias', None))

            if field_alias:
                result[field_alias] = result['count']

            return 200, result
        else:
            result = super(WebAPIResource, self).get_list(request,
                                                          *args, **kwargs)

            if request.GET.get('_first-result-only', False):
                # This is intended ONLY for deprecated functions. It is not
                # supported as a valid parameter for APIs and should be ignored.
                # It may return broken results if used outside of its built-in
                # intended uses.
                result = (result[0], {
                    self.name: result[1][self.name_plural][0],
                })

            return result

    def verify_fields(self, request, mutable_fields):
        invalid_fields = {}

        for field_name in request.POST:
            if field_name in ('action', 'method', 'callback'):
                # These are special names and can be ignored.
                continue

            if field_name not in mutable_fields:
                invalid_fields[field_name] = ['Field is not supported']

        for field_name in mutable_fields:
            if request.POST.get(field_name, None) is None:
                invalid_fields[field_name] = ['This field is required']

        return invalid_fields


class BaseCommentResource(WebAPIResource):
    model = Comment
    name = 'diff-comment'
    mutable_fields = ('first_line', 'num_lines', 'text')
    fields = mutable_fields + (
        'id', 'filediff', 'interfilediff', 'timestamp',
        'timesince', 'public', 'user',
    )

    uri_object_key = 'comment_id'

    allowed_methods = ('GET',)

    def get_queryset(self, request, review_request_id, *args, **kwargs):
        return self.model.objects.filter(
            Q(review__public=True) | Q(review__user=request.user),
            filediff__diffset__history__review_request=review_request_id,
            interfilediff__isnull=True)

    def serialize_public_field(self, obj):
        return obj.review.get().public

    def serialize_timesince_field(self, obj):
        return timesince(obj.timestamp)

    def serialize_user_field(self, obj):
        return obj.review.get().user


class FileDiffCommentResource(BaseCommentResource):
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')

    def get_queryset(self, request, review_request_id, diff_revision,
                     *args, **kwargs):
        return super(FileDiffCommentResource, self).get_queryset(
            request, review_request_id, *args, **kwargs).filter(
                filediff__diffset__revision=diff_revision)

    def get_href_parent_ids(self, comment, *args, **kwargs):
        filediff = comment.filediff
        diffset = filediff.diffset
        review_request = diffset.history.review_request.get()

        return {
            'review_request_id': review_request.id,
            'diff_revision': diffset.revision,
            'filediff_id': filediff.id,
        }

    @webapi_login_required
    def create(self, request, review_request_id, diff_revision, filediff_id,
               *args, **kwargs):
        try:
            review_request = ReviewRequest.objects.get(pk=review_request_id)
            filediff = FileDiff.objects.get(
                pk=filediff_id,
                diffset__revision=diff_revision,
                diffset__history__review_request=review_request)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        self._scan_deprecated_fields(request, **kwargs)

        invalid_fields = {}

        for field_name in request.POST:
{'fraction_non_alphanumeric': 0.07836005951396925, 'fraction_numerical': 0.000991899487518598, 'mean_word_length': 2.194877211513071, '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': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5914430', 'n_tokens_mistral': 3234, 'n_tokens_neox': 3090, 'n_words': 721}