Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 13 additions & 28 deletions pontoon/batch/actions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from django.db.models import QuerySet
from django.utils import timezone

from pontoon.actionlog.models import ActionLog
from pontoon.base.models import (
Translation,
TranslationMemoryEntry,
)
from pontoon.base.models import Entity, Locale, TranslationMemoryEntry
from pontoon.base.models.translation import Translation, TranslationQuerySet
from pontoon.batch import utils
from pontoon.messaging.notifications import send_badge_notification

Expand Down Expand Up @@ -42,7 +41,7 @@ def batch_action_template(form, user, translations, locale):
}


def approve_translations(form, user, translations, locale):
def approve_translations(user, locale: Locale, translations: TranslationQuerySet):
"""Approve a series of translations.

For documentation, refer to the `batch_action_template` function.
Expand Down Expand Up @@ -114,7 +113,7 @@ def approve_translations(form, user, translations, locale):
}


def reject_translations(form, user, translations, locale):
def reject_translations(user, locale: Locale, entities: QuerySet[Entity]):
"""Reject a series of translations.

Note that this function doesn't use the `translations` parameter, as it
Expand All @@ -126,7 +125,7 @@ def reject_translations(form, user, translations, locale):
"""
suggestions = Translation.objects.filter(
locale=locale,
entity__pk__in=form.cleaned_data["entities"],
entity__in=entities,
approved=False,
rejected=False,
)
Expand All @@ -145,7 +144,7 @@ def reject_translations(form, user, translations, locale):
performed_by=user,
translation=t,
)
for t in translations
for t in suggestions
]
ActionLog.objects.bulk_create(actions_to_log)

Expand Down Expand Up @@ -181,7 +180,9 @@ def reject_translations(form, user, translations, locale):
}


def replace_translations(form, user, translations, locale):
def replace_translations(
user, locale: Locale, translations: TranslationQuerySet, find: str, replace: str
):
"""Replace characters in a series of translations.

Replaces all occurences of the content of the `find` parameter with the
Expand All @@ -190,9 +191,6 @@ def replace_translations(form, user, translations, locale):
For documentation, refer to the `batch_action_template` function.

"""
find = form.cleaned_data["find"]
replace = form.cleaned_data["replace"]
latest_translation_pk = None

(
old_translations,
Expand Down Expand Up @@ -262,8 +260,9 @@ def replace_translations(form, user, translations, locale):

changed_translation_pks = [c.pk for c in changed_translations]

if changed_translation_pks:
latest_translation_pk = max(changed_translation_pks)
latest_translation_pk = (
max(changed_translation_pks) if changed_translation_pks else None
)

return {
"count": count,
Expand All @@ -274,17 +273,3 @@ def replace_translations(form, user, translations, locale):
"invalid_translation_pks": invalid_translation_pks,
"badge_update": badge_update,
}


"""A map of action names to functions.

The keys define the available batch actions in the `batch_edit_translations`
view. All functions must accept the same parameters and return the same dict.
See above for those functions.

"""
ACTIONS_FN_MAP = {
"approve": approve_translations,
"reject": reject_translations,
"replace": replace_translations,
}
5 changes: 3 additions & 2 deletions pontoon/batch/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
from django import forms

from pontoon.base import utils
from pontoon.batch.actions import ACTIONS_FN_MAP


class BatchActionsForm(forms.Form):
"""Handles the arguments passed to the batch actions view."""

locale = forms.CharField()
action = forms.ChoiceField(choices=[(x, x) for x in ACTIONS_FN_MAP.keys()])
action = forms.ChoiceField(
choices=[("approve", "approve"), ("reject", "reject"), ("replace", "replace")]
)
entities = forms.CharField(required=False)
find = forms.CharField(required=False)
replace = forms.CharField(required=False)
Expand Down
51 changes: 34 additions & 17 deletions pontoon/batch/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import logging

from typing import cast

from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import JsonResponse
Expand All @@ -11,13 +13,17 @@
Locale,
Project,
TranslatedResource,
Translation,
TranslationMemoryEntry,
)
from pontoon.base.models.translation import Translation, TranslationQuerySet
from pontoon.base.services import readonly_exists
from pontoon.base.utils import require_AJAX
from pontoon.batch import forms
from pontoon.batch.actions import ACTIONS_FN_MAP
from pontoon.batch.actions import (
approve_translations,
reject_translations,
replace_translations,
)


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -77,11 +83,12 @@ def batch_edit_translations(request):
# Also make sure projects are not enabled in read-only mode for a locale.
projects_pk = entities.values_list("resource__project__pk", flat=True)
projects = Project.objects.filter(pk__in=projects_pk.distinct())
user = request.user

for project in projects:
if not request.user.can_translate(
project=project, locale=locale
) or readonly_exists(projects, locale):
if not user.can_translate(project=project, locale=locale) or readonly_exists(
projects, locale
):
return JsonResponse(
{
"status": False,
Expand All @@ -90,20 +97,30 @@ def batch_edit_translations(request):
status=403,
)

active_translations = Translation.objects.filter(
active=True,
locale=locale,
entity__in=entities,
translations = cast(
TranslationQuerySet,
Translation.objects.filter(active=True, locale=locale, entity__in=entities),
)

# Execute the actual action.
action_function = ACTIONS_FN_MAP[form.cleaned_data["action"]]
action_status = action_function(
form,
request.user,
active_translations,
locale,
)
match form.cleaned_data["action"]:
case "approve":
action_status = approve_translations(user, locale, translations)
case "reject":
action_status = reject_translations(user, locale, entities)
case "replace":
action_status = replace_translations(
user,
locale,
translations,
form.cleaned_data["find"],
form.cleaned_data["replace"],
)
case _:
return JsonResponse(
{"status": False, "message": "Unsupported action"},
status=400,
)

if action_status.get("error"):
return JsonResponse(action_status)
Expand All @@ -122,7 +139,7 @@ def batch_edit_translations(request):
TranslatedResource.objects.filter(pk__in=tr_pks).calculate_stats()

# Mark translations as changed
active_translations.bulk_mark_changed()
translations.bulk_mark_changed()

# Reset term translations for entities belonging to the Terminology project
changed_entity_pks = [entity.pk for entity in action_status["changed_entities"]]
Expand Down
Loading