<commit_before>from __future__ import unicode_literals

import logging

import dateutil.parser
from django.contrib import auth
from django.contrib.auth.models import User
from django.core.exceptions import (PermissionDenied,
                                    ObjectDoesNotExist,
                                    ValidationError)
from django.db.models import Q
from django.utils import six
from djblets.util.decorators import augment_method_from
from djblets.webapi.decorators import (webapi_login_required,
                                       webapi_response_errors,
                                       webapi_request_fields)
from djblets.webapi.errors import (DOES_NOT_EXIST, NOT_LOGGED_IN,
                                   PERMISSION_DENIED)

from reviewboard.diffviewer.errors import (DiffTooBigError,
                                           DiffParserError,
                                           EmptyDiffError)
from reviewboard.reviews.errors import PermissionError
from reviewboard.reviews.models import ReviewRequest
from reviewboard.scmtools.errors import (AuthenticationError,
                                         ChangeNumberInUseError,
                                         EmptyChangeSetError,
                                         InvalidChangeNumberError,
                                         SCMError,
                                         RepositoryNotFoundError)
from reviewboard.ssh.errors import SSHError
from reviewboard.scmtools.models import Repository
from reviewboard.webapi.base import WebAPIResource
from reviewboard.webapi.decorators import webapi_check_local_site
from reviewboard.webapi.encoder import status_to_string, string_to_status
from reviewboard.webapi.errors import (CHANGE_NUMBER_IN_USE,
                                       COMMIT_ID_ALREADY_EXISTS,
                                       DIFF_EMPTY,
                                       DIFF_TOO_BIG,
                                       DIFF_PARSE_ERROR,
                                       EMPTY_CHANGESET,
                                       INVALID_CHANGE_NUMBER,
                                       INVALID_REPOSITORY,
                                       INVALID_USER,
                                       MISSING_REPOSITORY,
                                       REPO_AUTHENTICATION_ERROR,
                                       REPO_INFO_ERROR)
from reviewboard.webapi.mixins import MarkdownFieldsMixin
from reviewboard.webapi.resources import resources
from reviewboard.webapi.resources.repository import RepositoryResource
from reviewboard.webapi.resources.review_group import ReviewGroupResource
from reviewboard.webapi.resources.review_request_draft import \
    ReviewRequestDraftResource
from reviewboard.webapi.resources.user import UserResource


class ReviewRequestResource(MarkdownFieldsMixin, WebAPIResource):
    """Provides information on review requests.

    Review requests are one of the central concepts in Review Board. They
    represent code or files that are being placed up for review.

    A review request has a number of fields that can be filled out, indicating
    the summary, description of the change, testing that was done, affected
    bugs, and more. These must be filled out through the associated Review
    Request Draft resource.

    When a review request is published, it can be reviewed by users. It can
    then be updated, again through the Review Request Draft resource, or closed
    as submitted or discarded.

    If the ``text_type`` field is set to ``markdown``, then the ``description``
    and ``testing_done`` fields should be interpreted by the client as Markdown
    text.

    The returned text in the payload can be provided in a different format
    by passing ``?force-text-type=`` in the request. This accepts all the
    possible values listed in the ``text_type`` field below.
    """
    model = ReviewRequest
    name = 'review_request'

    fields = {
        'id': {
            'type': int,
            'description': 'The numeric ID of the review request.',
        },
        'approved': {
            'type': bool,
            'description': 'Whether the review request has been approved '
                           'by reviewers.\n'
                           '\n'
                           'On a default install, a review request is '
                           'approved if it has at least one Ship It! and '
                           'open issues. Extensions may change these '
                           'requirements.',
            'added_in': '2.0',
        },
        'approval_failure': {
            'type': six.text_type,
            'description': 'The reason why the review request was not '
                           'approved. This will be ``null`` if approved.',
            'added_in': '2.0',
        },
        'blocks': {
            'type': ['reviewboard.webapi.resources.review_request.'
                     'ReviewRequestResource'],
            'description': 'The list of review requests that this '
                           'review request is blocking.',
        },
        'depends_on': {
            'type': ['reviewboard.webapi.resources.review_request.'
                     'ReviewRequestResource'],
            'description': 'The list of review requests that this '
                           'review request depends on.',
        },
        'extra_data': {
            'type': dict,
            'description': 'Extra data as part of the review request. '
                           'This can be set by the API or extensions.',
            'added_in': '2.0',
        },
        'issue_dropped_count': {
            'type': int,
            'description': 'The number of dropped issues on this '
                           'review request',
            'added_in': '2.0',
        },
        'issue_open_count': {
            'type': int,
            'description': 'The number of open issues on this review request',
            'added_in': '2.0',
        },
        'issue_resolved_count': {
            'type': int,
            'description': 'The number of resolved issues on this '
                           'review request',
            'added_in': '2.0',
        },
        'submitter': {
            'type': UserResource,
            'description': 'The user who submitted the review request.',
        },
        'time_added': {
            'type': six.text_type,
            'description': 'The date and time that the review request was '
                           'added (in YYYY-MM-DD HH:MM:SS format).',
        },
        'last_updated': {
            'type': six.text_type,
            'description': 'The date and time that the review request was '
                           'last updated (in YYYY-MM-DD HH:MM:SS format).',
        },
        'text_type': {
            'type': MarkdownFieldsMixin.TEXT_TYPES,
            'description': 'The mode for the review request description '
                           'and testing_done fields.',
            'added_in': '2.0',
        },
        'status': {
            'type': ('discarded', 'pending', 'submitted'),
            'description': 'The current status of the review request.',
        },
        'public': {
            'type': bool,
            'description': 'Whether or not the review request is currently '
                           'visible to other users.',
        },
        'changenum': {
            'type': int,
            'description': 'The change number that the review request '
                           'represents. These are server-side repository-'
                           'specific change numbers, and are not supported '
                           'by all types of repositories. It may be '
                           '``null``.\n'
                           '\n'
                           'This is deprecated in favor of the ``commit_id`` '
                           'field.',
            'deprecated_in': '2.0',
        },
        'commit_id': {
            'type': six.text_type,
            'description': 'The commit that the review request represents. '
                           'This obsoletes the ``changenum`` field.',
            'added_in': '2.0',
        },
        'repository': {
            'type': RepositoryResource,
            'description': "The repository that the review request's code "
                           "is stored on.",
        },
        'ship_it_count': {
            'type': int,
            'description': 'The number of Ship Its given to this '
                           'review request.',
            'added_in': '2.0',
        },
        'summary': {
            'type': six.text_type,
            'description': "The review request's brief summary.",
        },
        'description': {
            'type': six.text_type,
            'description': "The review request's description.",
        },
        'testing_done': {
            'type': six.text_type,
            'description': 'The information on the testing that was done '
                           'for the change.',
        },
        'bugs_closed': {
            'type': [six.text_type],
            'description': 'The list of bugs closed or referenced by this '
                           'change.',
        },
        'branch': {
            'type': six.text_type,
            'description': 'The branch that the code was changed on or that '
                           'the code will be committed to. This is a '
                           'free-form field that can store any text.',
        },
        'target_groups': {
            'type': [ReviewGroupResource],
            'description': 'The list of review groups who were requested '
                           'to review this change.',
        },
        'target_people': {
            'type': [UserResource],
            'description': 'The list of users who were requested to review '
                           'this change.',
        },
        'url': {
            'type': six.text_type,
            'description': "The URL to the review request's page on the site. "
                           "This is deprecated and will be removed in a "
                           "future version.",
            'deprecated_in': '2.0',
        },
        'absolute_url': {
            'type': six.text_type,
            'description': "The absolute URL to the review request's page on "
                           "the site.",
            'added_in': '2.0',
        },
    }
    uri_object_key = 'review_request_id'
    model_object_key = 'display_id'
    last_modified_field = 'last_updated'
    item_child_resources = [
        resources.change,
        resources.diff,
        resources.diff_context,
        resources.review_request_draft,
        resources.review_request_last_update,
        resources.review,
        resources.screenshot,
        resources.file_attachment,
    ]

    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')

    _close_type_map = {
        'submitted': ReviewRequest.SUBMITTED,
        'discarded': ReviewRequest.DISCARDED,
    }

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

        By default, this returns all published or formerly published
        review requests.

        If the queryset is being used for a list of review request
        resources, then it can be further filtered by one or more arguments
        in the URL. These are listed in @webapi_request_fields for get_list().

        Some arguments accept dates. The handling of dates is quite flexible,
        accepting a variety of date/time formats, but we recommend sticking
        with ISO8601 format.

        ISO8601 format defines a date as being in ``{yyyy}-{mm}-{dd}`` format,
        and a date/time as being in ``{yyyy}-{mm}-{dd}T{HH}:{MM}:{SS}``.
        A timezone can also be appended to this, using ``-{HH:MM}``.

        The following examples are valid dates and date/times:

            * ``2010-06-27``
            * ``2010-06-27T16:26:30``
            * ``2010-06-27T16:26:30-08:00``
        """
        local_site = self._get_local_site(local_site_name)

        if is_list:
            q = Q()

            if 'to-groups' in request.GET:
                for group_name in request.GET.get('to-groups').split(','):
                    q = q & self.model.objects.get_to_group_query(group_name,
                                                                  None)

            if 'to-users' in request.GET:
                for username in request.GET.get('to-users').split(','):
                    q = q & self.model.objects.get_to_user_query(username)<commit_msg>Factor in the LocalSite when handing ?to-groups= on ReviewRequestResource.<commit_after>from __future__ import unicode_literals

import logging

import dateutil.parser
from django.contrib import auth
from django.contrib.auth.models import User
from django.core.exceptions import (PermissionDenied,
                                    ObjectDoesNotExist,
                                    ValidationError)
from django.db.models import Q
from django.utils import six
from djblets.util.decorators import augment_method_from
from djblets.webapi.decorators import (webapi_login_required,
                                       webapi_response_errors,
                                       webapi_request_fields)
from djblets.webapi.errors import (DOES_NOT_EXIST, NOT_LOGGED_IN,
                                   PERMISSION_DENIED)

from reviewboard.diffviewer.errors import (DiffTooBigError,
                                           DiffParserError,
                                           EmptyDiffError)
from reviewboard.reviews.errors import PermissionError
from reviewboard.reviews.models import ReviewRequest
from reviewboard.scmtools.errors import (AuthenticationError,
                                         ChangeNumberInUseError,
                                         EmptyChangeSetError,
                                         InvalidChangeNumberError,
                                         SCMError,
                                         RepositoryNotFoundError)
from reviewboard.ssh.errors import SSHError
from reviewboard.scmtools.models import Repository
from reviewboard.webapi.base import WebAPIResource
from reviewboard.webapi.decorators import webapi_check_local_site
from reviewboard.webapi.encoder import status_to_string, string_to_status
from reviewboard.webapi.errors import (CHANGE_NUMBER_IN_USE,
                                       COMMIT_ID_ALREADY_EXISTS,
                                       DIFF_EMPTY,
                                       DIFF_TOO_BIG,
                                       DIFF_PARSE_ERROR,
                                       EMPTY_CHANGESET,
                                       INVALID_CHANGE_NUMBER,
                                       INVALID_REPOSITORY,
                                       INVALID_USER,
                                       MISSING_REPOSITORY,
                                       REPO_AUTHENTICATION_ERROR,
                                       REPO_INFO_ERROR)
from reviewboard.webapi.mixins import MarkdownFieldsMixin
from reviewboard.webapi.resources import resources
from reviewboard.webapi.resources.repository import RepositoryResource
from reviewboard.webapi.resources.review_group import ReviewGroupResource
from reviewboard.webapi.resources.review_request_draft import \
    ReviewRequestDraftResource
from reviewboard.webapi.resources.user import UserResource


class ReviewRequestResource(MarkdownFieldsMixin, WebAPIResource):
    """Provides information on review requests.

    Review requests are one of the central concepts in Review Board. They
    represent code or files that are being placed up for review.

    A review request has a number of fields that can be filled out, indicating
    the summary, description of the change, testing that was done, affected
    bugs, and more. These must be filled out through the associated Review
    Request Draft resource.

    When a review request is published, it can be reviewed by users. It can
    then be updated, again through the Review Request Draft resource, or closed
    as submitted or discarded.

    If the ``text_type`` field is set to ``markdown``, then the ``description``
    and ``testing_done`` fields should be interpreted by the client as Markdown
    text.

    The returned text in the payload can be provided in a different format
    by passing ``?force-text-type=`` in the request. This accepts all the
    possible values listed in the ``text_type`` field below.
    """
    model = ReviewRequest
    name = 'review_request'

    fields = {
        'id': {
            'type': int,
            'description': 'The numeric ID of the review request.',
        },
        'approved': {
            'type': bool,
            'description': 'Whether the review request has been approved '
                           'by reviewers.\n'
                           '\n'
                           'On a default install, a review request is '
                           'approved if it has at least one Ship It! and '
                           'open issues. Extensions may change these '
                           'requirements.',
            'added_in': '2.0',
        },
        'approval_failure': {
            'type': six.text_type,
            'description': 'The reason why the review request was not '
                           'approved. This will be ``null`` if approved.',
            'added_in': '2.0',
        },
        'blocks': {
            'type': ['reviewboard.webapi.resources.review_request.'
                     'ReviewRequestResource'],
            'description': 'The list of review requests that this '
                           'review request is blocking.',
        },
        'depends_on': {
            'type': ['reviewboard.webapi.resources.review_request.'
                     'ReviewRequestResource'],
            'description': 'The list of review requests that this '
                           'review request depends on.',
        },
        'extra_data': {
            'type': dict,
            'description': 'Extra data as part of the review request. '
                           'This can be set by the API or extensions.',
            'added_in': '2.0',
        },
        'issue_dropped_count': {
            'type': int,
            'description': 'The number of dropped issues on this '
                           'review request',
            'added_in': '2.0',
        },
        'issue_open_count': {
            'type': int,
            'description': 'The number of open issues on this review request',
            'added_in': '2.0',
        },
        'issue_resolved_count': {
            'type': int,
            'description': 'The number of resolved issues on this '
                           'review request',
            'added_in': '2.0',
        },
        'submitter': {
            'type': UserResource,
            'description': 'The user who submitted the review request.',
        },
        'time_added': {
            'type': six.text_type,
            'description': 'The date and time that the review request was '
                           'added (in YYYY-MM-DD HH:MM:SS format).',
        },
        'last_updated': {
            'type': six.text_type,
            'description': 'The date and time that the review request was '
                           'last updated (in YYYY-MM-DD HH:MM:SS format).',
        },
        'text_type': {
            'type': MarkdownFieldsMixin.TEXT_TYPES,
            'description': 'The mode for the review request description '
                           'and testing_done fields.',
            'added_in': '2.0',
        },
        'status': {
            'type': ('discarded', 'pending', 'submitted'),
            'description': 'The current status of the review request.',
        },
        'public': {
            'type': bool,
            'description': 'Whether or not the review request is currently '
                           'visible to other users.',
        },
        'changenum': {
            'type': int,
            'description': 'The change number that the review request '
                           'represents. These are server-side repository-'
                           'specific change numbers, and are not supported '
                           'by all types of repositories. It may be '
                           '``null``.\n'
                           '\n'
                           'This is deprecated in favor of the ``commit_id`` '
                           'field.',
            'deprecated_in': '2.0',
        },
        'commit_id': {
            'type': six.text_type,
            'description': 'The commit that the review request represents. '
                           'This obsoletes the ``changenum`` field.',
            'added_in': '2.0',
        },
        'repository': {
            'type': RepositoryResource,
            'description': "The repository that the review request's code "
                           "is stored on.",
        },
        'ship_it_count': {
            'type': int,
            'description': 'The number of Ship Its given to this '
                           'review request.',
            'added_in': '2.0',
        },
        'summary': {
            'type': six.text_type,
            'description': "The review request's brief summary.",
        },
        'description': {
            'type': six.text_type,
            'description': "The review request's description.",
        },
        'testing_done': {
            'type': six.text_type,
            'description': 'The information on the testing that was done '
                           'for the change.',
        },
        'bugs_closed': {
            'type': [six.text_type],
            'description': 'The list of bugs closed or referenced by this '
                           'change.',
        },
        'branch': {
            'type': six.text_type,
            'description': 'The branch that the code was changed on or that '
                           'the code will be committed to. This is a '
                           'free-form field that can store any text.',
        },
        'target_groups': {
            'type': [ReviewGroupResource],
            'description': 'The list of review groups who were requested '
                           'to review this change.',
        },
        'target_people': {
            'type': [UserResource],
            'description': 'The list of users who were requested to review '
                           'this change.',
        },
        'url': {
            'type': six.text_type,
            'description': "The URL to the review request's page on the site. "
                           "This is deprecated and will be removed in a "
                           "future version.",
            'deprecated_in': '2.0',
        },
        'absolute_url': {
            'type': six.text_type,
            'description': "The absolute URL to the review request's page on "
                           "the site.",
            'added_in': '2.0',
        },
    }
    uri_object_key = 'review_request_id'
    model_object_key = 'display_id'
    last_modified_field = 'last_updated'
    item_child_resources = [
        resources.change,
        resources.diff,
        resources.diff_context,
        resources.review_request_draft,
        resources.review_request_last_update,
        resources.review,
        resources.screenshot,
        resources.file_attachment,
    ]

    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')

    _close_type_map = {
        'submitted': ReviewRequest.SUBMITTED,
        'discarded': ReviewRequest.DISCARDED,
    }

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

        By default, this returns all published or formerly published
        review requests.

        If the queryset is being used for a list of review request
        resources, then it can be further filtered by one or more arguments
        in the URL. These are listed in @webapi_request_fields for get_list().

        Some arguments accept dates. The handling of dates is quite flexible,
        accepting a variety of date/time formats, but we recommend sticking
        with ISO8601 format.

        ISO8601 format defines a date as being in ``{yyyy}-{mm}-{dd}`` format,
        and a date/time as being in ``{yyyy}-{mm}-{dd}T{HH}:{MM}:{SS}``.
        A timezone can also be appended to this, using ``-{HH:MM}``.

        The following examples are valid dates and date/times:

            * ``2010-06-27``
            * ``2010-06-27T16:26:30``
            * ``2010-06-27T16:26:30-08:00``
        """
        local_site = self._get_local_site(local_site_name)

        if is_list:
            q = Q()

            if 'to-groups' in request.GET:
                for group_name in request.GET.get('to-groups').split(','):
                    q = q & self.model.objects.get_to_group_query(group_name,
                                                                  local_site)

            if 'to-users' in request.GET:
                for username in request.GET.get('to-users').split(','):
                    q = q & self.model.objects.get_to_user_query(username)
{'fraction_non_alphanumeric': 0.08686465547599484, 'fraction_numerical': 0.0056344641389834485, 'mean_word_length': 1.5423256739281805, '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': 68, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4362210', 'n_tokens_mistral': 6317, 'n_tokens_neox': 6168, 'n_words': 1951}