<commit_before>import functools

from django import forms
from django.utils.translation import ugettext

from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

import olympia.core.logger

from olympia import amo
from olympia.access import acl
from olympia.addons.models import Addon
from olympia.amo.decorators import use_primary_db
from olympia.api.authentication import JWTKeyAuthentication
from olympia.devhub.views import handle_upload as devhub_handle_upload
from olympia.files.models import FileUpload
from olympia.files.utils import parse_addon
from olympia.signing.serializers import FileUploadSerializer
from olympia.versions import views as version_views
from olympia.versions.models import Version


log = olympia.core.logger.getLogger('signing')


def with_addon(allow_missing=False):
    """Call the view function with an addon instead of a guid. This will try
    find an addon with the guid and verify the user's permissions. If the
    add-on is not found it will 404 when allow_missing is False otherwise it
    will call the view with addon set to None."""
    def wrapper(fn):
        @functools.wraps(fn)
        def inner(view, request, **kwargs):
            guid = kwargs.get('guid', None)
            try:
                if guid is None:
                    raise Addon.DoesNotExist('No GUID')
                addon = Addon.unfiltered.get(guid=guid)
            except Addon.DoesNotExist:
                if allow_missing:
                    addon = None
                else:
                    msg = ugettext(
                        'Could not find add-on with id "{}".').format(guid)
                    return Response(
                        {'error': msg},
                        status=status.HTTP_404_NOT_FOUND)
            # Call the view if there is no add-on, the current user is an
            # author of the add-on or the current user is an admin and the
            # request is a GET.
            has_perm = (
                addon is None or
                (addon.has_author(request.user) or
                    (request.method == 'GET' and
                        acl.action_allowed_user(
                            request.user, amo.permissions.ADDONS_EDIT))))

            if has_perm:
                return fn(view, request, addon=addon, **kwargs)
            else:
                return Response(
                    {'error': ugettext('You do not own this addon.')},
                    status=status.HTTP_403_FORBIDDEN)
        return inner
    return wrapper


class VersionView(APIView):
    authentication_classes = [JWTKeyAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):
        version_string = request.data.get('version', None)

        try:
            file_upload, _ = self.handle_upload(request, None, version_string)
        except forms.ValidationError as exc:
            return Response(
                {'error': exc.message},
                status=exc.code or status.HTTP_400_BAD_REQUEST)

        serializer = FileUploadSerializer(
            file_upload, context={'request': request})
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @with_addon(allow_missing=True)
    def put(self, request, addon, version_string, guid=None):
        try:
            file_upload, created = self.handle_upload(
                request, addon, version_string, guid=guid)
        except forms.ValidationError as exc:
            return Response(
                {'error': exc.message},
                status=exc.code or status.HTTP_400_BAD_REQUEST)

        status_code = (
            status.HTTP_201_CREATED if created else status.HTTP_202_ACCEPTED)

        serializer = FileUploadSerializer(
            file_upload, context={'request': request})
        return Response(serializer.data, status=status_code)

    @use_primary_db
    def handle_upload(self, request, addon, version_string, guid=None):
        if 'upload' in request.FILES:
            filedata = request.FILES['upload']
        else:
            raise forms.ValidationError(
                ugettext(u'Missing "upload" key in multipart file data.'),
                status.HTTP_400_BAD_REQUEST)

        # # Parse the file to get and validate package data with the addon.
        parsed_data = parse_addon(filedata, addon, user=request.user)

        if addon is not None and addon.status == amo.STATUS_DISABLED:
            msg = ugettext(
                'You cannot add versions to an addon that has status: %s.'
                % amo.STATUS_CHOICES_ADDON[amo.STATUS_DISABLED])
            raise forms.ValidationError(msg, status.HTTP_400_BAD_REQUEST)

        version_string = version_string or parsed_data['version']

        if version_string and parsed_data['version'] != version_string:
            raise forms.ValidationError(
                ugettext('Version does not match the manifest file.'),
                status.HTTP_400_BAD_REQUEST)

        if (addon is not None and
                addon.versions.filter(version=version_string).exists()):
            raise forms.ValidationError(
                ugettext('Version already exists.'),
                status.HTTP_409_CONFLICT)

        package_guid = parsed_data.get('guid', None)

        dont_allow_no_guid = (
            not addon and not package_guid and
            not parsed_data.get('is_webextension', False))

        if dont_allow_no_guid:
            raise forms.ValidationError(
                ugettext('Only WebExtensions are allowed to omit the GUID'),
                status.HTTP_400_BAD_REQUEST)
<commit_msg>Add latest version hint for version exists error<commit_after>import functools

from django import forms
from django.utils.translation import ugettext

from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

import olympia.core.logger

from olympia import amo
from olympia.access import acl
from olympia.addons.models import Addon
from olympia.amo.decorators import use_primary_db
from olympia.api.authentication import JWTKeyAuthentication
from olympia.devhub.views import handle_upload as devhub_handle_upload
from olympia.files.models import FileUpload
from olympia.files.utils import parse_addon
from olympia.signing.serializers import FileUploadSerializer
from olympia.versions import views as version_views
from olympia.versions.models import Version


log = olympia.core.logger.getLogger('signing')


def with_addon(allow_missing=False):
    """Call the view function with an addon instead of a guid. This will try
    find an addon with the guid and verify the user's permissions. If the
    add-on is not found it will 404 when allow_missing is False otherwise it
    will call the view with addon set to None."""
    def wrapper(fn):
        @functools.wraps(fn)
        def inner(view, request, **kwargs):
            guid = kwargs.get('guid', None)
            try:
                if guid is None:
                    raise Addon.DoesNotExist('No GUID')
                addon = Addon.unfiltered.get(guid=guid)
            except Addon.DoesNotExist:
                if allow_missing:
                    addon = None
                else:
                    msg = ugettext(
                        'Could not find add-on with id "{}".').format(guid)
                    return Response(
                        {'error': msg},
                        status=status.HTTP_404_NOT_FOUND)
            # Call the view if there is no add-on, the current user is an
            # author of the add-on or the current user is an admin and the
            # request is a GET.
            has_perm = (
                addon is None or
                (addon.has_author(request.user) or
                    (request.method == 'GET' and
                        acl.action_allowed_user(
                            request.user, amo.permissions.ADDONS_EDIT))))

            if has_perm:
                return fn(view, request, addon=addon, **kwargs)
            else:
                return Response(
                    {'error': ugettext('You do not own this addon.')},
                    status=status.HTTP_403_FORBIDDEN)
        return inner
    return wrapper


class VersionView(APIView):
    authentication_classes = [JWTKeyAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):
        version_string = request.data.get('version', None)

        try:
            file_upload, _ = self.handle_upload(request, None, version_string)
        except forms.ValidationError as exc:
            return Response(
                {'error': exc.message},
                status=exc.code or status.HTTP_400_BAD_REQUEST)

        serializer = FileUploadSerializer(
            file_upload, context={'request': request})
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @with_addon(allow_missing=True)
    def put(self, request, addon, version_string, guid=None):
        try:
            file_upload, created = self.handle_upload(
                request, addon, version_string, guid=guid)
        except forms.ValidationError as exc:
            return Response(
                {'error': exc.message},
                status=exc.code or status.HTTP_400_BAD_REQUEST)

        status_code = (
            status.HTTP_201_CREATED if created else status.HTTP_202_ACCEPTED)

        serializer = FileUploadSerializer(
            file_upload, context={'request': request})
        return Response(serializer.data, status=status_code)

    @use_primary_db
    def handle_upload(self, request, addon, version_string, guid=None):
        if 'upload' in request.FILES:
            filedata = request.FILES['upload']
        else:
            raise forms.ValidationError(
                ugettext(u'Missing "upload" key in multipart file data.'),
                status.HTTP_400_BAD_REQUEST)

        # # Parse the file to get and validate package data with the addon.
        parsed_data = parse_addon(filedata, addon, user=request.user)

        if addon is not None and addon.status == amo.STATUS_DISABLED:
            msg = ugettext(
                'You cannot add versions to an addon that has status: %s.'
                % amo.STATUS_CHOICES_ADDON[amo.STATUS_DISABLED])
            raise forms.ValidationError(msg, status.HTTP_400_BAD_REQUEST)

        version_string = version_string or parsed_data['version']

        if version_string and parsed_data['version'] != version_string:
            raise forms.ValidationError(
                ugettext('Version does not match the manifest file.'),
                status.HTTP_400_BAD_REQUEST)

        if (addon is not None and
                addon.versions.filter(version=version_string).exists()):
            latest_version = addon.find_latest_version(None, exclude=())
            msg = ugettext('Version already exists. Latest version is: %s.'
                 % latest_version)
            raise forms.ValidationError(msg, status.HTTP_409_CONFLICT)

        package_guid = parsed_data.get('guid', None)

        dont_allow_no_guid = (
            not addon and not package_guid and
            not parsed_data.get('is_webextension', False))

        if dont_allow_no_guid:
            raise forms.ValidationError(
                ugettext('Only WebExtensions are allowed to omit the GUID'),
                status.HTTP_400_BAD_REQUEST)

{'fraction_non_alphanumeric': 0.07392426850258176, 'fraction_numerical': 0.006712564543889845, 'mean_word_length': 2.2271591224659817, '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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5259376', 'n_tokens_mistral': 3190, 'n_tokens_neox': 3049, 'n_words': 890}