diff --git a/pontoon/batch/actions.py b/pontoon/batch/actions.py index e01a3ced5f..62c6fb3ce4 100644 --- a/pontoon/batch/actions.py +++ b/pontoon/batch/actions.py @@ -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 @@ -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. @@ -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 @@ -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, ) @@ -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) @@ -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 @@ -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, @@ -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, @@ -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, -} diff --git a/pontoon/batch/forms.py b/pontoon/batch/forms.py index e80cd4df3d..5d3582968c 100644 --- a/pontoon/batch/forms.py +++ b/pontoon/batch/forms.py @@ -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) diff --git a/pontoon/batch/views.py b/pontoon/batch/views.py index 9650201cc4..b3c00beb40 100644 --- a/pontoon/batch/views.py +++ b/pontoon/batch/views.py @@ -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 @@ -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__) @@ -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, @@ -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) @@ -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"]]