<commit_before>import logging
import re
import urllib

import dateutil.parser
from django.conf import settings
from django.contrib import auth
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.http import HttpResponseRedirect, HttpResponse
from django.template.defaultfilters import timesince
from django.utils.translation import ugettext as _
from djblets.siteconfig.models import SiteConfiguration
from djblets.util.http import get_http_requested_mimetype, \
                              set_last_modified
from djblets.webapi.core import WebAPIResponseFormError, \
                                WebAPIResponsePaginated, \
                                WebAPIResponse
from djblets.webapi.decorators import webapi_login_required, \
                                      webapi_request_fields
from djblets.webapi.errors import DOES_NOT_EXIST, INVALID_FORM_DATA, \
                                  PERMISSION_DENIED
from djblets.webapi.resources import WebAPIResource as DjbletsWebAPIResource, \
                                     UserResource as DjbletsUserResource, \
                                     RootResource, \
                                     register_resource_for_model, \
                                     get_resource_for_object

from reviewboard import get_version_string, get_package_version, is_release
from reviewboard.accounts.models import Profile
from reviewboard.diffviewer.diffutils import get_diff_files
from reviewboard.diffviewer.forms import EmptyDiffError
from reviewboard.reviews.errors import PermissionError
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 CHANGE_NUMBER_IN_USE, \
                                      EMPTY_CHANGESET, \
                                      INVALID_CHANGE_NUMBER, \
                                      INVALID_REPOSITORY, \
                                      INVALID_USER, \
                                      REPO_FILE_NOT_FOUND, \
                                      REPO_INFO_ERROR, \
                                      REPO_NOT_IMPLEMENTED


CUSTOM_MIMETYPE_BASE = 'application/vnd.reviewboard.org'


class WebAPIResource(DjbletsWebAPIResource):
    """A specialization of the Djblets WebAPIResource for Review Board."""

    @webapi_check_login_required
    def get(self, request, *args, **kwargs):
        """Returns the serialized object for the resource.

        This will require login if anonymous access isn't enabled on the
        site.
        """
        return super(WebAPIResource, self).get(request, *args, **kwargs)

    @webapi_check_login_required
    def get_list(self, request, *args, **kwargs):
        """Returns a list of objects.

        This will require login if anonymous access isn't enabled on the
        site.

        If ``?counts-only=1`` is passed on the URL, then this will return
        only a ``count`` field with the number of entries, instead of the
        serialized objects.
        """
        if self.model and request.GET.get('counts-only', False):
            return 200, {
                'count': self.get_queryset(request, is_list=True,
                                           *args, **kwargs).count()
            }
        else:
            return super(WebAPIResource, self).get_list(request,
                                                        *args, **kwargs)


class BaseCommentResource(WebAPIResource):
    """Base class for diff comment resources.

    Provides common fields and functionality for all diff comment resources.
    """
    model = Comment
    name = 'diff_comment'
    fields = (
        'id', 'first_line', 'num_lines', 'text', 'filediff',
        'interfilediff', 'timestamp', 'timesince', 'public', 'user',
    )

    uri_object_key = 'comment_id'

    allowed_methods = ('GET',)

    def get_queryset(self, request, review_request_id, *args, **kwargs):
        """Returns a queryset for Comment models.

        This filters the query for comments on the specified review request
        which are either public or owned by the requesting user.
        """
        return self.model.objects.filter(
            Q(review__public=True) | Q(review__user=request.user),
            filediff__diffset__history__review_request=review_request_id)

    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):
    """A resource representing diff comments inside a filediff resource.

    This resource is read-only, and only handles returning the list of
    comments. All comment creation is handled by ReviewCommentResource.
    """
    allowed_methods = ('GET',)
    model_parent_key = 'filediff'

    def get_queryset(self, request, review_request_id, diff_revision,
                     is_list=False, *args, **kwargs):
        """Returns a queryset for Comment models.

        This filters the query for comments on the specified review request
        and made on the specified diff revision, which are either public or
        owned by the requesting user.

        If the queryset is being used for a list of comment resources,
        then this can be further filtered by passing ``?interdiff_revision=``
        on the URL to match the given interdiff revision, and
        ``?line=`` to match comments on the given line number.
        """
        q = super(FileDiffCommentResource, self).get_queryset(
            request, review_request_id, *args, **kwargs)
        q = q.filter(filediff__diffset__revision=diff_revision)

        if is_list:
            if 'interdiff_revision' in request.GET:
                interdiff_revision = int(request.GET['interdiff_revision'])
                q = q.filter(
                    interfilediff__diffset__revision=interdiff_revision)

            if 'line' in request.GET:
                q = q.filter(first_line=int(request.GET['line']))

        return q

filediff_comment_resource = FileDiffCommentResource()


class ReviewCommentResource(BaseCommentResource):
    """A resource representing diff comments on a review."""
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
    model_parent_key = 'review'

    def get_queryset(self, request, review_request_id, review_id,
                     is_list=False, *args, **kwargs):
        """Returns a queryset for Comment models.

        This filters the query for comments on the particular review.

        If the queryset is being used for a list of comment resources,
        then this can be further filtered by passing ``?interdiff_revision=``
        on the URL to match the given interdiff revision, and
        ``?line=`` to match comments on the given line number.
        """
        q = super(ReviewCommentResource, self).get_queryset(
            request, review_request_id, *args, **kwargs)
        q = q.filter(review=review_id)

        if is_list:
            if 'interdiff_revision' in request.GET:
                interdiff_revision = int(request.GET['interdiff_revision'])
                q = q.filter(
                    interfilediff__diffset__revision=interdiff_revision)

            if 'line' in request.GET:
                q = q.filter(first_line=int(request.GET['line']))

        return q

    def has_delete_permissions(self, request, comment, *args, **kwargs):
        review = comment.review.get()
        return not review.public and review.user == request.user

    @webapi_login_required
    @webapi_request_fields(
        required = {
            'filediff_id': {
                'type': int,
                'description': 'The ID of the file diff the comment is on.',
            },
            'first_line': {
                'type': int,
                'description': 'The line number the comment starts at.',
            },<commit_msg>Consolidate the logic for diff comment list filtering.<commit_after>import logging
import re
import urllib

import dateutil.parser
from django.conf import settings
from django.contrib import auth
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.http import HttpResponseRedirect, HttpResponse
from django.template.defaultfilters import timesince
from django.utils.translation import ugettext as _
from djblets.siteconfig.models import SiteConfiguration
from djblets.util.http import get_http_requested_mimetype, \
                              set_last_modified
from djblets.webapi.core import WebAPIResponseFormError, \
                                WebAPIResponsePaginated, \
                                WebAPIResponse
from djblets.webapi.decorators import webapi_login_required, \
                                      webapi_request_fields
from djblets.webapi.errors import DOES_NOT_EXIST, INVALID_FORM_DATA, \
                                  PERMISSION_DENIED
from djblets.webapi.resources import WebAPIResource as DjbletsWebAPIResource, \
                                     UserResource as DjbletsUserResource, \
                                     RootResource, \
                                     register_resource_for_model, \
                                     get_resource_for_object

from reviewboard import get_version_string, get_package_version, is_release
from reviewboard.accounts.models import Profile
from reviewboard.diffviewer.diffutils import get_diff_files
from reviewboard.diffviewer.forms import EmptyDiffError
from reviewboard.reviews.errors import PermissionError
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 CHANGE_NUMBER_IN_USE, \
                                      EMPTY_CHANGESET, \
                                      INVALID_CHANGE_NUMBER, \
                                      INVALID_REPOSITORY, \
                                      INVALID_USER, \
                                      REPO_FILE_NOT_FOUND, \
                                      REPO_INFO_ERROR, \
                                      REPO_NOT_IMPLEMENTED


CUSTOM_MIMETYPE_BASE = 'application/vnd.reviewboard.org'


class WebAPIResource(DjbletsWebAPIResource):
    """A specialization of the Djblets WebAPIResource for Review Board."""

    @webapi_check_login_required
    def get(self, request, *args, **kwargs):
        """Returns the serialized object for the resource.

        This will require login if anonymous access isn't enabled on the
        site.
        """
        return super(WebAPIResource, self).get(request, *args, **kwargs)

    @webapi_check_login_required
    def get_list(self, request, *args, **kwargs):
        """Returns a list of objects.

        This will require login if anonymous access isn't enabled on the
        site.

        If ``?counts-only=1`` is passed on the URL, then this will return
        only a ``count`` field with the number of entries, instead of the
        serialized objects.
        """
        if self.model and request.GET.get('counts-only', False):
            return 200, {
                'count': self.get_queryset(request, is_list=True,
                                           *args, **kwargs).count()
            }
        else:
            return super(WebAPIResource, self).get_list(request,
                                                        *args, **kwargs)


class BaseCommentResource(WebAPIResource):
    """Base class for diff comment resources.

    Provides common fields and functionality for all diff comment resources.
    """
    model = Comment
    name = 'diff_comment'
    fields = (
        'id', 'first_line', 'num_lines', 'text', 'filediff',
        'interfilediff', 'timestamp', 'timesince', 'public', 'user',
    )

    uri_object_key = 'comment_id'

    allowed_methods = ('GET',)

    def get_queryset(self, request, review_request_id, is_list=False,
                     *args, **kwargs):
        """Returns a queryset for Comment models.

        This filters the query for comments on the specified review request
        which are either public or owned by the requesting user.

        If the queryset is being used for a list of comment resources,
        then this can be further filtered by passing ``?interdiff-revision=``
        on the URL to match the given interdiff revision, and
        ``?line=`` to match comments on the given line number.
        """
        q = self.model.objects.filter(
            Q(review__public=True) | Q(review__user=request.user),
            filediff__diffset__history__review_request=review_request_id)

        if is_list:
            if 'interdiff-revision' in request.GET:
                interdiff_revision = int(request.GET['interdiff-revision'])
                q = q.filter(
                    interfilediff__diffset__revision=interdiff_revision)

            if 'line' in request.GET:
                q = q.filter(first_line=int(request.GET['line']))

        return q

    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):
    """A resource representing diff comments inside a filediff resource.

    This resource is read-only, and only handles returning the list of
    comments. All comment creation is handled by ReviewCommentResource.
    """
    allowed_methods = ('GET',)
    model_parent_key = 'filediff'

    def get_queryset(self, request, review_request_id, diff_revision,
                     *args, **kwargs):
        """Returns a queryset for Comment models.

        This filters the query for comments on the specified review request
        and made on the specified diff revision, which are either public or
        owned by the requesting user.

        If the queryset is being used for a list of comment resources,
        then this can be further filtered by passing ``?interdiff-revision=``
        on the URL to match the given interdiff revision, and
        ``?line=`` to match comments on the given line number.
        """
        q = super(FileDiffCommentResource, self).get_queryset(
            request, review_request_id, *args, **kwargs)
        return q.filter(filediff__diffset__revision=diff_revision)

filediff_comment_resource = FileDiffCommentResource()


class ReviewCommentResource(BaseCommentResource):
    """A resource representing diff comments on a review."""
    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
    model_parent_key = 'review'

    def get_queryset(self, request, review_request_id, review_id,
                     *args, **kwargs):
        q = super(ReviewCommentResource, self).get_queryset(
            request, review_request_id, *args, **kwargs)
        return q.filter(review=review_id)

    def has_delete_permissions(self, request, comment, *args, **kwargs):
        review = comment.review.get()
        return not review.public and review.user == request.user

    @webapi_login_required
    @webapi_request_fields(
        required = {
            'filediff_id': {
                'type': int,
                'description': 'The ID of the file diff the comment is on.',
            },
            'first_line': {
                'type': int,
                'description': 'The line number the comment starts at.',
            },
{'fraction_non_alphanumeric': 0.07189081068360983, 'fraction_numerical': 0.0004706436051300153, 'mean_word_length': 2.1497128034093014, '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': 46, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2952676', 'n_tokens_mistral': 4358, 'n_tokens_neox': 4182, 'n_words': 1257}