Exemple #1
0
def test_ignore_warnings(entity_properties_plurals_mock, ):
    """
    Check if logic of ignore_warnings works when there are errors.
    """
    assert run_checks(
        entity_properties_plurals_mock,
        "en-US",
        entity_properties_plurals_mock.string,
        "plural1;plural2;plural3;plural4;plural5",
        True,
    ) == {
        "clWarnings": ["expecting 2 plurals, found 5"],
        "ttWarnings": ["Simple capitalization", "Starting capitalization"],
    }

    # Warnings can be ignored for Translate Toolkit if user decides to do so
    assert run_checks(
        entity_properties_plurals_mock,
        "en-US",
        entity_properties_plurals_mock.string,
        "plural1;plural2;plural3;plural4;plural5",
        False,
    ) == {
        "clWarnings": ["expecting 2 plurals, found 5"]
    }
Exemple #2
0
def test_tt_disabled_checks(
    entity_properties_mock,
    entity_dtd_mock,
    run_tt_checks_mock,
):
    """
    Check if overlapping checks are disabled in Translate Toolkit.
    """
    assert run_checks(
        entity_properties_mock,
        'en-US',
        entity_properties_mock.string,
        'invalid translation \q',
        True,
        False,
    ).content == json.dumps({
        'failedChecks': {
            'clWarnings': ['unknown escape sequence, \q']
        },
    })
    run_tt_checks_mock.assert_called_with(ANY, ANY, ANY,
                                          {'escapes', 'nplurals', 'printf'})

    assert run_checks(entity_dtd_mock, 'en-US', entity_properties_mock.string,
                      'Translated string', True, False) is None
    assert not run_tt_checks_mock.assert_called_with(
        ANY, ANY, ANY, {'acronyms', 'gconf', 'kdecomments'})
Exemple #3
0
def test_ignore_warnings(entity_properties_plurals_mock, ):
    """
    Check if logic of ignore_warnings works when there are errors.
    """
    assert run_checks(
        entity_properties_plurals_mock,
        'en-US',
        entity_properties_plurals_mock.string,
        'plural1;plural2;plural3;plural4;plural5',
        True,
        False,
    ).content == json.dumps({
        'failedChecks': {
            'clWarnings': ['expecting 2 plurals, found 5'],
            'ttWarnings': ['Simple capitalization', 'Starting capitalization']
        }
    })

    # Warnings can be ignored for Translate Toolkit if user decides to do so
    assert run_checks(
        entity_properties_plurals_mock,
        'en-US',
        entity_properties_plurals_mock.string,
        'plural1;plural2;plural3;plural4;plural5',
        False,
        False,
    ).content == json.dumps(
        {'failedChecks': {
            'clWarnings': ['expecting 2 plurals, found 5'],
        }})
Exemple #4
0
def test_ignore_warnings(
    entity_properties_plurals_mock,
):
    """
    Check if logic of ignore_warnings works when there are errors.
    """
    assert run_checks(
        entity_properties_plurals_mock,
        'en-US',
        entity_properties_plurals_mock.string,
        'plural1;plural2;plural3;plural4;plural5',
        True,
    ) == {
        'clWarnings': ['expecting 2 plurals, found 5'],
        'ttWarnings': ['Simple capitalization', 'Starting capitalization']
    }

    # Warnings can be ignored for Translate Toolkit if user decides to do so
    assert run_checks(
        entity_properties_plurals_mock,
        'en-US',
        entity_properties_plurals_mock.string,
        'plural1;plural2;plural3;plural4;plural5',
        False,
    ) == {
        'clWarnings': ['expecting 2 plurals, found 5'],
    }
Exemple #5
0
def test_invalid_resource_compare_locales(entity_invalid_resource_mock, ):
    """
    Unsupported resource shouldn't raise an error.
    """
    assert run_checks(entity_invalid_resource_mock, 'en-US',
                      entity_invalid_resource_mock.string, 'Translation',
                      False, False) is None
Exemple #6
0
def perform_checks(request):
    """Perform quality checks and return a list of any failed ones."""
    try:
        entity = request.POST['entity']
        locale_code = request.POST['locale_code']
        original = request.POST['original']
        string = request.POST['string']
        ignore_warnings = request.POST.get('ignore_warnings', 'false') == 'true'
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        entity = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        use_ttk_checks = UserProfile.objects.get(user=request.user).quality_checks
    except UserProfile.DoesNotExist:
        use_ttk_checks = True

    failed_checks = run_checks(
        entity,
        locale_code,
        original,
        string,
        use_ttk_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({
            'failedChecks': failed_checks,
        })
    else:
        return HttpResponse('ok')
Exemple #7
0
def perform_checks(request):
    """Perform quality checks and return a list of any failed ones."""
    try:
        entity = request.POST["entity"]
        locale_code = request.POST["locale_code"]
        original = request.POST["original"]
        string = request.POST["string"]
        ignore_warnings = request.POST.get("ignore_warnings", "false") == "true"
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {"status": False, "message": "Bad Request: {error}".format(error=e)},
            status=400,
        )

    try:
        entity = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return JsonResponse(
            {"status": False, "message": "Bad Request: {error}".format(error=e)},
            status=400,
        )

    failed_checks = run_checks(
        entity, locale_code, original, string, request.user.profile.quality_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({"failedChecks": failed_checks})
    else:
        return JsonResponse({"status": True})
Exemple #8
0
def bulk_run_checks(translations):
    """
    Run checks on a list of translations

    *Important*
    To avoid performance problems, translations have to prefetch entities and locales objects.
    """
    warnings, errors = [], []
    if not translations:
        return

    for translation in translations:
        warnings_, errors_ = get_failed_checks_db_objects(
            translation,
            run_checks(translation.entity,
                       translation.locale.code,
                       translation.entity.string,
                       translation.string,
                       use_tt_checks=False))
        warnings.extend(warnings_)
        errors.extend(errors_)

    # Remove old warnings and errors
    Warning.objects.filter(
        translation__pk__in=[t.pk for t in translations]).delete()
    Error.objects.filter(translation__pk__in=[t.pk
                                              for t in translations]).delete()

    # Insert new warnings and errors
    Warning.objects.bulk_create(warnings)
    Error.objects.bulk_create(errors)

    return warnings, errors
Exemple #9
0
def perform_checks(request):
    """Perform quality checks and return a list of any failed ones."""
    try:
        entity = request.POST['entity']
        locale_code = request.POST['locale_code']
        original = request.POST['original']
        string = request.POST['string']
        ignore_warnings = request.POST.get('ignore_warnings', 'false') == 'true'
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        entity = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        use_ttk_checks = UserProfile.objects.get(user=request.user).quality_checks
    except UserProfile.DoesNotExist:
        use_ttk_checks = True

    failed_checks = run_checks(
        entity,
        locale_code,
        original,
        string,
        use_ttk_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({
            'failedChecks': failed_checks,
        })
    else:
        return HttpResponse('ok')
Exemple #10
0
def test_tt_checks_simple_ftl(
    entity_ftl_mock,
    run_tt_checks_mock,
):
    """
    Check translate toolkit checks for a simple ftl entity
    """

    translated_string = dedent("""
    windowTitle = Translated string
        .pontoon = is kewl
    """)

    run_checks(
        entity_ftl_mock,
        "en-US",
        entity_ftl_mock.string,
        translated_string,
        True,
    )

    run_tt_checks_mock.assert_called_with(
        "Untranslated string is cool",
        "Translated string is kewl",
        ANY,
        {
            "acronyms",
            "gconf",
            "kdecomments",
            "untranslated",
            "doublespacing",
            "endwhitespace",
            "escapes",
            "newlines",
            "numbers",
            "printf",
            "singlequoting",
            "startwhitespace",
            "pythonbraceformat",
            "doublequoting",
        },
        None,
        {"varmatches": [("{$", "}")]},
    )
Exemple #11
0
def test_invalid_resource_compare_locales(entity_invalid_resource_mock, ):
    """
    Unsupported resource shouldn't raise an error.
    """
    assert (run_checks(
        entity_invalid_resource_mock,
        "en-US",
        entity_invalid_resource_mock.string,
        "Translation",
        False,
    ) == {})
Exemple #12
0
def test_tt_disabled_checks(
    entity_properties_mock,
    entity_dtd_mock,
    run_tt_checks_mock,
):
    """
    Check if overlapping checks are disabled in Translate Toolkit.
    """
    assert run_checks(
        entity_properties_mock,
        "en-US",
        entity_properties_mock.string,
        "invalid translation \\q",
        True,
    ) == {
        "clWarnings": ["unknown escape sequence, \\q"]
    }
    run_tt_checks_mock.assert_called_with(
        ANY,
        ANY,
        ANY,
        {
            "escapes",
            "acronyms",
            "printf",
            "gconf",
            "kdecomments",
            "nplurals",
            "untranslated",
        },
    )

    assert (run_checks(
        entity_dtd_mock,
        "en-US",
        entity_properties_mock.string,
        "Translated string",
        True,
    ) == {})
    assert not run_tt_checks_mock.assert_called_with(
        ANY, ANY, ANY, {"acronyms", "gconf", "kdecomments", "untranslated"})
Exemple #13
0
def test_tt_disabled_checks(
    entity_properties_mock,
    entity_dtd_mock,
    run_tt_checks_mock,
):
    """
    Check if overlapping checks are disabled in Translate Toolkit.
    """
    assert run_checks(
        entity_properties_mock,
        'en-US',
        entity_properties_mock.string,
        'invalid translation \q',
        True,
    ) == {
        'clWarnings': [
            'unknown escape sequence, \q'
        ]
    }
    run_tt_checks_mock.assert_called_with(
        ANY,
        ANY,
        ANY,
        {'escapes', 'acronyms', 'printf', 'gconf', 'kdecomments', 'nplurals'},
    )

    assert run_checks(
        entity_dtd_mock,
        'en-US',
        entity_properties_mock.string,
        'Translated string',
        True,
    ) == {}
    assert not run_tt_checks_mock.assert_called_with(
        ANY,
        ANY,
        ANY,
        {'acronyms', 'gconf', 'kdecomments'}
    )
Exemple #14
0
def test_invalid_resource_compare_locales(
    entity_invalid_resource_mock,
):
    """
    Unsupported resource shouldn't raise an error.
    """
    assert run_checks(
        entity_invalid_resource_mock,
        'en-US',
        entity_invalid_resource_mock.string,
        'Translation',
        False
    ) == {}
Exemple #15
0
def test_tt_disabled_checks(
    entity_properties_mock,
    entity_dtd_mock,
    run_tt_checks_mock,
):
    """
    Check if overlapping checks are disabled in Translate Toolkit.
    """
    assert run_checks(
        entity_properties_mock,
        'en-US',
        entity_properties_mock.string,
        'invalid translation \q',
        True,
    ) == {
        'clWarnings': ['unknown escape sequence, \q']
    }
    run_tt_checks_mock.assert_called_with(
        ANY,
        ANY,
        ANY,
        {
            'escapes', 'acronyms', 'printf', 'gconf', 'kdecomments',
            'nplurals', 'untranslated'
        },
    )

    assert run_checks(
        entity_dtd_mock,
        'en-US',
        entity_properties_mock.string,
        'Translated string',
        True,
    ) == {}
    assert not run_tt_checks_mock.assert_called_with(
        ANY, ANY, ANY, {'acronyms', 'gconf', 'kdecomments', 'untranslated'})
Exemple #16
0
def perform_checks(request):
    """Perform quality checks and return a list of any failed ones."""
    try:
        entity = request.POST['entity']
        locale_code = request.POST['locale_code']
        original = request.POST['original']
        string = request.POST['string']
        ignore_warnings = request.POST.get('ignore_warnings',
                                           'false') == 'true'
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                'status': False,
                'message': 'Bad Request: {error}'.format(error=e),
            },
            status=400)

    try:
        entity = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return JsonResponse(
            {
                'status': False,
                'message': 'Bad Request: {error}'.format(error=e),
            },
            status=400)

    failed_checks = run_checks(
        entity,
        locale_code,
        original,
        string,
        request.user.profile.quality_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({
            'failedChecks': failed_checks,
        })
    else:
        return JsonResponse({
            'status': True,
        })
Exemple #17
0
def perform_checks(request):
    """Perform quality checks and return a list of any failed ones."""
    try:
        entity = request.POST['entity']
        locale_code = request.POST['locale_code']
        original = request.POST['original']
        string = request.POST['string']
        ignore_warnings = request.POST.get('ignore_warnings', 'false') == 'true'
    except MultiValueDictKeyError as e:
        return JsonResponse({
            'status': False,
            'message': 'Bad Request: {error}'.format(error=e),
        }, status=400)

    try:
        entity = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return JsonResponse({
            'status': False,
            'message': 'Bad Request: {error}'.format(error=e),
        }, status=400)

    failed_checks = run_checks(
        entity,
        locale_code,
        original,
        string,
        request.user.profile.quality_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({
            'failedChecks': failed_checks,
        })
    else:
        return JsonResponse({
            'status': True,
        })
Exemple #18
0
def approve_translation(request):
    """Approve given translation."""
    try:
        t = request.POST["translation"]
        ignore_warnings = request.POST.get("ignore_warnings",
                                           "false") == "true"
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    translation = get_object_or_404(Translation, pk=t)
    entity = translation.entity
    project = entity.resource.project
    locale = translation.locale
    user = request.user

    # Read-only translations cannot be approved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    if translation.approved:
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This translation is already approved.",
            },
            status=403,
        )

    # Only privileged users can approve translations
    if not user.can_translate(locale, project):
        return JsonResponse(
            {
                "status":
                False,
                "message":
                "Forbidden: You don't have permission to approve this translation.",
            },
            status=403,
        )

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"

    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            entity.string,
            translation.string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                "string": translation.string,
                "failedChecks": failed_checks
            })

    translation.approve(user)

    log_action("translation:approved", user, translation=translation)

    active_translation = translation.entity.reset_active_translation(
        locale=locale,
        plural_form=translation.plural_form,
    )

    return JsonResponse({
        "translation":
        active_translation.serialize(),
        "stats":
        TranslatedResource.objects.stats(project, paths, locale),
    })
Exemple #19
0
def update_translation(request):
    """Update entity translation for the specified locale and user.

    Note that this view is also used to approve a translation by the old
    Translate app. Once we migrate to Translate.Next, we'll want to rework
    this view to remove the bits about approving a translation, because that
    has been delegated to the `approve_translation` view.

    """
    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_warnings = request.POST.get('ignore_warnings',
                                           'false') == 'true'
        approve = request.POST.get('approve', 'false') == 'true'
        force_suggestions = request.POST.get('force_suggestions',
                                             'false') == 'true'
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                'status': False,
                'message': 'Bad Request: {error}'.format(error=e),
            },
            status=400)

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return JsonResponse(
            {
                'status': False,
                'message': 'Bad Request: {error}'.format(error=e),
            },
            status=400)

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as e:
        return JsonResponse(
            {
                'status': False,
                'message': 'Bad Request: {error}'.format(error=e),
            },
            status=400)

    if plural_form == "-1":
        plural_form = None

    user = request.user
    project = e.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                'status': False,
                'message': 'Forbidden: This string is in read-only mode.',
            },
            status=403)

    now = timezone.now()
    can_translate = (request.user.can_translate(project=project, locale=locale)
                     and (not force_suggestions or approve))
    translations = Translation.objects.filter(entity=e,
                                              locale=locale,
                                              plural_form=plural_form)

    same_translations = translations.filter(string=string).order_by(
        '-active', 'rejected', '-date')

    # If same translation exists in the DB, don't save it again.
    if utils.is_same(same_translations, can_translate):
        return JsonResponse({
            'same': True,
        })

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != 'tutorial'

    if use_checks:
        failed_checks = run_checks(
            e,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                'failedChecks': failed_checks,
            })

    # Translations exist
    if len(translations) > 0:
        # Same translation exists
        if len(same_translations) > 0:
            t = same_translations[0]

            # If added by privileged user, approve and unfuzzy it
            if can_translate and (t.fuzzy or not t.approved):
                if not t.active:
                    translations.filter(active=True).update(active=False)
                    t.active = True

                t.approved = True
                t.fuzzy = False
                t.rejected = False
                t.rejected_user = None
                t.rejected_date = None

                if t.approved_user is None:
                    t.approved_user = user
                    t.approved_date = now

                t.save(failed_checks=failed_checks)

                return JsonResponse({
                    'type':
                    'updated',
                    'translation':
                    t.serialize(),
                    'stats':
                    TranslatedResource.objects.stats(project, paths, locale),
                })

        # Different translation added
        else:
            t = Translation(
                entity=e,
                locale=locale,
                plural_form=plural_form,
                string=string,
                user=user,
                date=now,
                approved=can_translate,
            )

            if can_translate:
                t.approved_user = user
                t.approved_date = now

            t.save(failed_checks=failed_checks)

            active_translation = e.reset_active_translation(
                locale=locale,
                plural_form=plural_form,
            )

            return JsonResponse({
                'type':
                'added',
                'translation':
                active_translation.serialize(),
                'stats':
                TranslatedResource.objects.stats(project, paths, locale),
            })

    # No translations saved yet
    else:
        t = Translation(
            entity=e,
            locale=locale,
            plural_form=plural_form,
            string=string,
            user=user,
            date=now,
            active=True,
            approved=can_translate,
        )

        if can_translate:
            t.approved_user = user
            t.approved_date = now

        t.save(failed_checks=failed_checks)

        return JsonResponse({
            'type':
            'saved',
            'translation':
            t.serialize(),
            'stats':
            TranslatedResource.objects.stats(project, paths, locale),
        })
Exemple #20
0
def approve_translation(request):
    """Approve given translation."""
    try:
        t = request.POST['translation']
        ignore_warnings = request.POST.get('ignore_warnings',
                                           'false') == 'true'
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                'status': False,
                'message': 'Bad Request: {error}'.format(error=e),
            },
            status=400)

    translation = get_object_or_404(Translation, pk=t)
    entity = translation.entity
    project = entity.resource.project
    locale = translation.locale
    user = request.user

    # Read-only translations cannot be approved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                'status': False,
                'message': 'Forbidden: This string is in read-only mode.',
            },
            status=403)

    if translation.approved:
        return JsonResponse(
            {
                'status': False,
                'message': 'Forbidden: This translation is already approved.',
            },
            status=403)

    # Only privileged users can approve translations
    if not user.can_translate(locale, project):
        return JsonResponse(
            {
                'status':
                False,
                'message':
                "Forbidden: You don't have permission to approve this translation.",
            },
            status=403)

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != 'tutorial'

    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            entity.string,
            translation.string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                'string': translation.string,
                'failedChecks': failed_checks,
            })

    translation.approve(user)

    active_translation = translation.entity.reset_active_translation(
        locale=locale,
        plural_form=translation.plural_form,
    )

    return JsonResponse({
        'translation':
        active_translation.serialize(),
        'stats':
        TranslatedResource.objects.stats(project, paths, locale),
    })
Exemple #21
0
def approve_translation(request):
    """Approve given translation."""
    try:
        t = request.POST['translation']
        ignore_warnings = request.POST.get('ignore_warnings', 'false') == 'true'
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return JsonResponse({
            'status': False,
            'message': 'Bad Request: {error}'.format(error=e),
        }, status=400)

    translation = get_object_or_404(Translation, pk=t)
    entity = translation.entity
    project = entity.resource.project
    locale = translation.locale
    user = request.user

    # Read-only translations cannot be approved
    if utils.readonly_exists(project, locale):
        return JsonResponse({
            'status': False,
            'message': 'Forbidden: This string is in read-only mode.',
        }, status=403)

    if translation.approved:
        return JsonResponse({
            'status': False,
            'message': 'Forbidden: This translation is already approved.',
        }, status=403)

    # Only privileged users can approve translations
    if not user.can_translate(locale, project):
        return JsonResponse({
            'status': False,
            'message': "Forbidden: You don't have permission to approve this translation.",
        }, status=403)

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != 'tutorial'

    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            entity.string,
            translation.string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                'string': translation.string,
                'failedChecks': failed_checks,
            })

    translation.approve(user)

    active_translation = translation.entity.reset_active_translation(
        locale=locale,
        plural_form=translation.plural_form,
    )

    return JsonResponse({
        'translation': active_translation.serialize(),
        'stats': TranslatedResource.objects.stats(project, paths, locale),
    })
Exemple #22
0
def update_translation(request):
    """Update entity translation for the specified locale and user."""

    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_warnings = request.POST.get('ignore_warnings', 'false') == 'true'
        approve = request.POST.get('approve', 'false') == 'true'
        force_suggestions = request.POST.get('force_suggestions', 'false') == 'true'
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    if plural_form == "-1":
        plural_form = None

    user = request.user
    project = e.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return HttpResponseForbidden(
            "Forbidden: This string is in read-only mode"
        )

    try:
        use_ttk_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        use_ttk_checks = True

    # Disable checks for tutorial project.
    if project.slug == 'tutorial':
        use_ttk_checks = False

    now = timezone.now()
    can_translate = (
        request.user.can_translate(project=project, locale=locale) and
        (not force_suggestions or approve)
    )
    translations = Translation.objects.filter(
        entity=e, locale=locale, plural_form=plural_form)

    same_translations = translations.filter(string=string).order_by(
        '-approved', 'rejected', '-date'
    )

    # If same translation exists in the DB, don't save it again.
    if utils.is_same(same_translations, can_translate):
        return JsonResponse({
            'same': True,
        })

    failed_checks = run_checks(
        e,
        locale.code,
        original,
        string,
        use_ttk_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({
            'failedChecks': failed_checks,
        })

    # Translations exist
    if len(translations) > 0:
        if len(same_translations) > 0:
            t = same_translations[0]

            # If added by privileged user, approve and unfuzzy it
            if can_translate and (t.fuzzy or not t.approved):
                translations.update(
                    approved=False,
                    approved_user=None,
                    approved_date=None,
                    rejected=True,
                    rejected_user=request.user,
                    rejected_date=timezone.now(),
                    fuzzy=False,
                )

                t.approved = True
                t.fuzzy = False
                t.rejected = False
                t.rejected_user = None
                t.rejected_date = None

                if t.approved_user is None:
                    t.approved_user = user
                    t.approved_date = now

            # If added by non-privileged user and fuzzy, unfuzzy it
            elif t.fuzzy:
                t.approved = False
                t.approved_user = None
                t.approved_date = None
                t.fuzzy = False

            t.save()

            t.warnings.all().delete()
            t.errors.all().delete()
            save_failed_checks(t, failed_checks)

            return JsonResponse({
                'type': 'updated',
                'translation': t.serialize(),
                'stats': TranslatedResource.objects.stats(project, paths, locale),
            })

        # Different translation added
        else:
            if can_translate:
                translations.update(approved=False, approved_user=None, approved_date=None)

            translations.update(fuzzy=False)

            t = Translation(
                entity=e, locale=locale, user=user, string=string,
                plural_form=plural_form, date=now,
                approved=can_translate)

            if can_translate:
                t.approved_user = user
                t.approved_date = now

            t.save()
            save_failed_checks(t, failed_checks)

            # Return active (approved or latest) translation
            try:
                active = translations.filter(approved=True).latest("date")
            except Translation.DoesNotExist:
                active = translations.latest("date")

            return JsonResponse({
                'type': 'added',
                'translation': active.serialize(),
                'stats': TranslatedResource.objects.stats(project, paths, locale),
            })

    # No translations saved yet
    else:
        t = Translation(
            entity=e, locale=locale, user=user, string=string,
            plural_form=plural_form, date=now,
            approved=can_translate)

        if can_translate:
            t.approved_user = user
            t.approved_date = now

        t.save()
        save_failed_checks(t, failed_checks)

        return JsonResponse({
            'type': 'saved',
            'translation': t.serialize(),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
        })
Exemple #23
0
def create_translation(request):
    """
    Create a new translation.
    """
    form = forms.CreateTranslationForm(request.POST)

    if not form.is_valid():
        problems = []
        for field, errors in form.errors.items():
            problems.append('Error validating field `{0}`: "{1}"'.format(
                field, " ".join(errors)))
        return JsonResponse({
            "status": False,
            "message": "\n".join(problems)
        },
                            status=400)

    entity = form.cleaned_data["entity"]
    string = form.cleaned_data["translation"]
    locale = form.cleaned_data["locale"]
    plural_form = form.cleaned_data["plural_form"]
    original = form.cleaned_data["original"]
    ignore_warnings = form.cleaned_data["ignore_warnings"]
    approve = form.cleaned_data["approve"]
    force_suggestions = form.cleaned_data["force_suggestions"]
    resources = form.cleaned_data["paths"]

    project = entity.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    translations = Translation.objects.filter(entity=entity,
                                              locale=locale,
                                              plural_form=plural_form)

    same_translations = translations.filter(string=string)

    # If same translation exists in the DB, don't save it again.
    if same_translations:
        return JsonResponse({"status": False, "same": True})

    # Look for failed checks.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"
    user = request.user

    failed_checks = None
    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                "status": False,
                "failedChecks": failed_checks
            })

    now = timezone.now()
    can_translate = user.can_translate(
        project=project, locale=locale) and (not force_suggestions or approve)

    translation = Translation(
        entity=entity,
        locale=locale,
        plural_form=plural_form,
        string=string,
        user=user,
        date=now,
        approved=can_translate,
    )

    if can_translate:
        translation.approved_user = user
        translation.approved_date = now

    translation.save(failed_checks=failed_checks)

    log_action("translation:created", user, translation=translation)

    if translations:
        translation = entity.reset_active_translation(
            locale=locale,
            plural_form=plural_form,
        )

    return JsonResponse({
        "status":
        True,
        "translation":
        translation.serialize(),
        "stats":
        TranslatedResource.objects.stats(project, resources, locale),
    })
Exemple #24
0
def find_and_replace(translations, find, replace, user):
    """Replace text in a set of translation.

    :arg QuerySet translations: a list of Translation objects in which to search
    :arg string find: a string to search for, and replace, in translations
    :arg string replace: what to replace the original string with
    :arg User user: the User making the change

    :returns: a tuple with:
        - a queryset of old translations to be changed
        - a list of new translations to be created
        - a list of PKs of translations with errors

    """
    translations = translations.filter(string__contains=find)

    # No matches found
    if translations.count() == 0:
        return translations, [], []

    # Create translations' clones and replace strings
    now = timezone.now()
    translations_to_create = []
    translations_with_errors = []

    # To speed-up error checks, translations will prefetch additional fields
    translations = (
        translations.for_checks(only_db_formats=False)
    )

    for translation in translations:
        # Cache the old value to identify changed translations
        string = translation.string
        old_translation_pk = translation.pk

        if translation.entity.resource.format == 'ftl':
            translation.string = ftl_find_and_replace(string, find, replace)
        else:
            translation.string = string.replace(find, replace)

        # Quit early if no changes are made
        if translation.string == string:
            continue

        translation.pk = None  # Create new translation
        translation.user = translation.approved_user = user
        translation.date = translation.approved_date = now
        translation.approved = True
        translation.rejected = False
        translation.rejected_date = None
        translation.rejected_user = None
        translation.fuzzy = False

        if translation.entity.resource.format in DB_FORMATS:
            errors = run_checks(
                translation.entity,
                translation.locale.code,
                translation.entity.string,
                translation.string,
                use_tt_checks=False,
            )
        else:
            errors = {}

        if errors:
            translations_with_errors.append(old_translation_pk)
        else:
            translations_to_create.append(translation)

    if translations_with_errors:
        translations = translations.exclude(
            pk__in=translations_with_errors
        )

    return (
        translations,
        translations_to_create,
        translations_with_errors,
    )
Exemple #25
0
def find_and_replace(translations, find, replace, user):
    """Replace text in a set of translation.

    :arg QuerySet translations: a list of Translation objects in which to search
    :arg string find: a string to search for, and replace, in translations
    :arg string replace: what to replace the original string with
    :arg User user: the User making the change

    :returns: a tuple with:
        - a queryset of old translations to be changed
        - a list of new translations to be created
        - a list of PKs of translations with errors

    """
    translations = translations.filter(string__contains=find)

    # No matches found
    if translations.count() == 0:
        return translations, [], []

    # Create translations' clones and replace strings
    now = timezone.now()
    translations_to_create = []
    translations_with_errors = []

    # To speed-up error checks, translations will prefetch additional fields
    translations = translations.for_checks(only_db_formats=False)

    for translation in translations:
        # Cache the old value to identify changed translations
        new_translation = deepcopy(translation)

        if translation.entity.resource.format == "ftl":
            new_translation.string = ftl_find_and_replace(
                translation.string, find, replace)
        else:
            new_translation.string = translation.string.replace(find, replace)

        # Quit early if no changes are made
        if new_translation.string == translation.string:
            continue

        new_translation.pk = None  # Create new translation
        new_translation.user = new_translation.approved_user = user
        new_translation.date = new_translation.approved_date = now
        new_translation.approved = True
        new_translation.rejected = False
        new_translation.rejected_date = None
        new_translation.rejected_user = None
        new_translation.fuzzy = False

        if new_translation.entity.resource.format in DB_FORMATS:
            errors = run_checks(
                new_translation.entity,
                new_translation.locale.code,
                new_translation.entity.string,
                new_translation.string,
                use_tt_checks=False,
            )
        else:
            errors = {}

        if errors:
            translations_with_errors.append(translation.pk)
        else:
            translations_to_create.append(new_translation)

    if translations_with_errors:
        translations = translations.exclude(pk__in=translations_with_errors)

    return (
        translations,
        translations_to_create,
        translations_with_errors,
    )
Exemple #26
0
def update_translation(request):
    """Update entity translation for the specified locale and user.

    Note that this view is also used to approve a translation by the old
    Translate app. Once we migrate to Translate.Next, we'll want to rework
    this view to remove the bits about approving a translation, because that
    has been delegated to the `approve_translation` view.

    """
    try:
        entity = request.POST["entity"]
        string = request.POST["translation"]
        locale = request.POST["locale"]
        plural_form = request.POST["plural_form"]
        original = request.POST["original"]
        ignore_warnings = request.POST.get("ignore_warnings",
                                           "false") == "true"
        approve = request.POST.get("approve", "false") == "true"
        force_suggestions = request.POST.get("force_suggestions",
                                             "false") == "true"
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    if plural_form == "-1":
        plural_form = None

    user = request.user
    project = e.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    now = timezone.now()
    can_translate = request.user.can_translate(
        project=project, locale=locale) and (not force_suggestions or approve)
    translations = Translation.objects.filter(entity=e,
                                              locale=locale,
                                              plural_form=plural_form)

    same_translations = translations.filter(string=string).order_by(
        "-active", "rejected", "-date")

    # If same translation exists in the DB, don't save it again.
    if utils.is_same(same_translations, can_translate):
        return JsonResponse({"same": True})

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"

    failed_checks = None
    if use_checks:
        failed_checks = run_checks(
            e,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({"failedChecks": failed_checks})

    # Translations exist
    if len(translations) > 0:
        # Same translation exists
        if len(same_translations) > 0:
            t = same_translations[0]

            # If added by privileged user, approve and unfuzzy it
            if can_translate and (t.fuzzy or not t.approved):
                if not t.active:
                    translations.filter(active=True).update(active=False)
                    t.active = True

                t.approved = True
                t.fuzzy = False
                t.rejected = False
                t.rejected_user = None
                t.rejected_date = None

                if t.approved_user is None:
                    t.approved_user = user
                    t.approved_date = now

                t.save(failed_checks=failed_checks)

                log_action("translation:approved", user, translation=t)

                return JsonResponse({
                    "type":
                    "updated",
                    "translation":
                    t.serialize(),
                    "stats":
                    TranslatedResource.objects.stats(project, paths, locale),
                })

        # Different translation added
        else:
            t = Translation(
                entity=e,
                locale=locale,
                plural_form=plural_form,
                string=string,
                user=user,
                date=now,
                approved=can_translate,
            )

            if can_translate:
                t.approved_user = user
                t.approved_date = now

            t.save(failed_checks=failed_checks)

            log_action("translation:created", user, translation=t)

            active_translation = e.reset_active_translation(
                locale=locale,
                plural_form=plural_form,
            )

            return JsonResponse({
                "type":
                "added",
                "translation":
                active_translation.serialize(),
                "stats":
                TranslatedResource.objects.stats(project, paths, locale),
            })

    # No translations saved yet
    else:
        t = Translation(
            entity=e,
            locale=locale,
            plural_form=plural_form,
            string=string,
            user=user,
            date=now,
            active=True,
            approved=can_translate,
        )

        if can_translate:
            t.approved_user = user
            t.approved_date = now

        t.save(failed_checks=failed_checks)

        log_action("translation:created", user, translation=t)

        return JsonResponse({
            "type":
            "saved",
            "translation":
            t.serialize(),
            "stats":
            TranslatedResource.objects.stats(project, paths, locale),
        })
Exemple #27
0
def create_translation(request):
    """
    Create a new translation.
    """
    form = forms.CreateTranslationForm(request.POST)

    if not form.is_valid():
        problems = []
        for field, errors in form.errors.items():
            problems.append(
                'Error validating field `{}`: "{}"'.format(field, " ".join(errors))
            )
        return JsonResponse(
            {"status": False, "message": "\n".join(problems)}, status=400
        )

    entity = form.cleaned_data["entity"]
    string = form.cleaned_data["translation"]
    locale = form.cleaned_data["locale"]
    plural_form = form.cleaned_data["plural_form"]
    original = form.cleaned_data["original"]
    ignore_warnings = form.cleaned_data["ignore_warnings"]
    approve = form.cleaned_data["approve"]
    force_suggestions = form.cleaned_data["force_suggestions"]
    paths = form.cleaned_data["paths"]
    machinery_sources = form.cleaned_data["machinery_sources"]

    project = entity.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    translations = Translation.objects.filter(
        entity=entity,
        locale=locale,
        plural_form=plural_form,
    )

    same_translations = translations.filter(string=string)

    # If same translation exists in the DB, don't save it again.
    if same_translations:
        return JsonResponse({"status": False, "same": True})

    # Look for failed checks.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"
    user = request.user
    first_contribution = user.is_new_contributor(locale)

    failed_checks = None
    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({"status": False, "failedChecks": failed_checks})

    now = timezone.now()
    can_translate = user.can_translate(project=project, locale=locale) and (
        not force_suggestions or approve
    )

    translation = Translation(
        entity=entity,
        locale=locale,
        plural_form=plural_form,
        string=string,
        user=user,
        date=now,
        approved=can_translate,
        machinery_sources=machinery_sources,
    )

    if can_translate:
        translation.approved_user = user
        translation.approved_date = now

    translation.save(failed_checks=failed_checks)

    log_action(ActionLog.ActionType.TRANSLATION_CREATED, user, translation=translation)

    if translations:
        translation = entity.reset_active_translation(
            locale=locale,
            plural_form=plural_form,
        )

    # When user makes their first contribution to the team, notify team managers
    if first_contribution:
        desc = """
        <a href="{user_href}">{user}</a> has made their first contribution to
        <a href="{locale_href}">{locale} ({locale_code})</a>.
        Please welcome them to the team, and make sure to
        <a href="{review_href}">review their suggestions</a>.
        """.format(
            user=user.name_or_email,
            user_href=reverse(
                "pontoon.contributors.contributor.username",
                kwargs={
                    "username": user.username,
                },
            ),
            locale=locale.name,
            locale_code=locale.code,
            locale_href=reverse(
                "pontoon.teams.team",
                kwargs={
                    "locale": locale.code,
                },
            ),
            review_href=reverse(
                "pontoon.translate",
                kwargs={
                    "locale": locale.code,
                    "project": project.slug,
                    "resource": entity.resource.path,
                },
            )
            + f"?string={entity.pk}",
        )

        for manager in locale.managers_group.user_set.filter(
            profile__new_contributor_notifications=True
        ):
            notify.send(
                sender=manager,
                recipient=manager,
                verb="has reviewed suggestions",  # Triggers render of description only
                description=desc,
            )

    return JsonResponse(
        {
            "status": True,
            "translation": translation.serialize(),
            "stats": TranslatedResource.objects.stats(project, paths, locale),
        }
    )
Exemple #28
0
def update_translation(request):
    """Update entity translation for the specified locale and user.

    Note that this view is also used to approve a translation by the old
    Translate app. Once we migrate to Translate.Next, we'll want to rework
    this view to remove the bits about approving a translation, because that
    has been delegated to the `approve_translation` view.

    """
    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_warnings = request.POST.get('ignore_warnings', 'false') == 'true'
        approve = request.POST.get('approve', 'false') == 'true'
        force_suggestions = request.POST.get('force_suggestions', 'false') == 'true'
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    if plural_form == "-1":
        plural_form = None

    user = request.user
    project = e.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return HttpResponseForbidden(
            "Forbidden: This string is in read-only mode"
        )

    try:
        use_ttk_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        use_ttk_checks = True

    # Disable checks for tutorial project.
    if project.slug == 'tutorial':
        use_ttk_checks = False

    now = timezone.now()
    can_translate = (
        request.user.can_translate(project=project, locale=locale) and
        (not force_suggestions or approve)
    )
    translations = Translation.objects.filter(
        entity=e, locale=locale, plural_form=plural_form)

    same_translations = translations.filter(string=string).order_by(
        '-active', 'rejected', '-date'
    )

    # If same translation exists in the DB, don't save it again.
    if utils.is_same(same_translations, can_translate):
        return JsonResponse({
            'same': True,
        })

    failed_checks = run_checks(
        e,
        locale.code,
        original,
        string,
        use_ttk_checks,
    )

    if are_blocking_checks(failed_checks, ignore_warnings):
        return JsonResponse({
            'failedChecks': failed_checks,
        })

    # Translations exist
    if len(translations) > 0:
        # Same translation exists
        if len(same_translations) > 0:
            t = same_translations[0]

            # If added by privileged user, approve and unfuzzy it
            if can_translate and (t.fuzzy or not t.approved):
                if not t.active:
                    translations.filter(active=True).update(active=False)
                    t.active = True

                t.approved = True
                t.fuzzy = False
                t.rejected = False
                t.rejected_user = None
                t.rejected_date = None

                if t.approved_user is None:
                    t.approved_user = user
                    t.approved_date = now

                t.save()

                t.warnings.all().delete()
                t.errors.all().delete()
                save_failed_checks(t, failed_checks)

                return JsonResponse({
                    'type': 'updated',
                    'translation': t.serialize(),
                    'stats': TranslatedResource.objects.stats(project, paths, locale),
                })

        # Different translation added
        else:
            t = Translation(
                entity=e,
                locale=locale,
                plural_form=plural_form,
                string=string,
                user=user,
                date=now,
                approved=can_translate,
            )

            if can_translate:
                t.approved_user = user
                t.approved_date = now

            t.save()
            save_failed_checks(t, failed_checks)

            active_translation = e.reset_active_translation(
                locale=locale,
                plural_form=plural_form,
            )

            return JsonResponse({
                'type': 'added',
                'translation': active_translation.serialize(),
                'stats': TranslatedResource.objects.stats(project, paths, locale),
            })

    # No translations saved yet
    else:
        t = Translation(
            entity=e,
            locale=locale,
            plural_form=plural_form,
            string=string,
            user=user,
            date=now,
            active=True,
            approved=can_translate,
        )

        if can_translate:
            t.approved_user = user
            t.approved_date = now

        t.save()
        save_failed_checks(t, failed_checks)

        return JsonResponse({
            'type': 'saved',
            'translation': t.serialize(),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
        })