Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality for counting batch actions #3499

Merged
merged 5 commits into from
Jan 6, 2025
Merged
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
20 changes: 20 additions & 0 deletions pontoon/base/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,24 @@ def badges_promotion_count(self):
)


@property
def badges_translation_level(self):
thresholds = settings.BADGES_TRANSLATION_THRESHOLDS
for level in range(len(thresholds) - 1):
if thresholds[level] <= self.badges_translation_count < thresholds[level + 1]:
return level + 1
return 0


@property
def badges_review_level(self):
thresholds = settings.BADGES_REVIEW_THRESHOLDS
for level in range(len(thresholds) - 1):
if thresholds[level] <= self.badges_review_count < thresholds[level + 1]:
return level + 1
return 0


@property
def top_contributed_locale(self):
"""Locale the user has made the most contributions to."""
Expand Down Expand Up @@ -523,6 +541,8 @@ def latest_action(self):
User.add_to_class("badges_translation_count", badges_translation_count)
User.add_to_class("badges_review_count", badges_review_count)
User.add_to_class("badges_promotion_count", badges_promotion_count)
User.add_to_class("badges_translation_level", badges_translation_level)
harmitgoswami marked this conversation as resolved.
Show resolved Hide resolved
User.add_to_class("badges_review_level", badges_review_level)
User.add_to_class("has_approved_translations", has_approved_translations)
User.add_to_class("top_contributed_locale", top_contributed_locale)
User.add_to_class("can_translate", can_translate)
Expand Down
34 changes: 34 additions & 0 deletions pontoon/batch/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
TranslationMemoryEntry,
)
from pontoon.batch import utils
from pontoon.messaging.notifications import send_badge_notification


def batch_action_template(form, user, translations, locale):
Expand Down Expand Up @@ -69,6 +70,8 @@ def approve_translations(form, user, translations, locale):
locale,
)

before_level = user.badges_review_level

# Log approving actions
actions_to_log = [
ActionLog(
Expand All @@ -80,6 +83,14 @@ def approve_translations(form, user, translations, locale):
]
ActionLog.objects.bulk_create(actions_to_log)

# Send Review Master Badge notification information
after_level = user.badges_review_level
badge_update = {}
if after_level > before_level:
badge_update["level"] = after_level
badge_update["name"] = "Review Master"
send_badge_notification(user, badge_update["name"], badge_update["level"])

# Approve translations.
translations.update(
approved=True,
Expand All @@ -99,6 +110,7 @@ def approve_translations(form, user, translations, locale):
"latest_translation_pk": latest_translation_pk,
"changed_translation_pks": changed_translation_pks,
"invalid_translation_pks": invalid_translation_pks,
"badge_update": badge_update,
}


Expand All @@ -124,6 +136,8 @@ def reject_translations(form, user, translations, locale):
)
TranslationMemoryEntry.objects.filter(translation__in=suggestions).delete()

before_level = user.badges_review_level

# Log rejecting actions
actions_to_log = [
ActionLog(
Expand All @@ -135,6 +149,14 @@ def reject_translations(form, user, translations, locale):
]
ActionLog.objects.bulk_create(actions_to_log)

# Send Review Master Badge notification information
after_level = user.badges_review_level
badge_update = {}
if after_level > before_level:
badge_update["level"] = after_level
badge_update["name"] = "Review Master"
send_badge_notification(user, badge_update["name"], badge_update["level"])

# Reject translations.
suggestions.update(
active=False,
Expand All @@ -155,6 +177,7 @@ def reject_translations(form, user, translations, locale):
"latest_translation_pk": None,
"changed_translation_pks": [],
"invalid_translation_pks": [],
"badge_update": badge_update,
}


Expand Down Expand Up @@ -216,6 +239,8 @@ def replace_translations(form, user, translations, locale):
translations_to_create,
)

before_level = user.badges_translation_level

# Log creating actions
actions_to_log = [
ActionLog(
Expand All @@ -227,6 +252,14 @@ def replace_translations(form, user, translations, locale):
]
ActionLog.objects.bulk_create(actions_to_log)

# Send Translation Champion Badge notification information
after_level = user.badges_translation_level
badge_update = {}
if after_level > before_level:
badge_update["level"] = after_level
badge_update["name"] = "Translation Champion"
send_badge_notification(user, badge_update["name"], badge_update["level"])

changed_translation_pks = [c.pk for c in changed_translations]

if changed_translation_pks:
Expand All @@ -239,6 +272,7 @@ def replace_translations(form, user, translations, locale):
"latest_translation_pk": latest_translation_pk,
"changed_translation_pks": changed_translation_pks,
"invalid_translation_pks": invalid_translation_pks,
"badge_update": badge_update,
}


Expand Down
4 changes: 4 additions & 0 deletions pontoon/batch/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def test_batch_approve_valid_translations(
assert response.json() == {
"count": 1,
"invalid_translation_count": 0,
"badge_update": {},
}

translation_dtd_unapproved.refresh_from_db()
Expand All @@ -170,6 +171,7 @@ def test_batch_approve_invalid_translations(
assert response.json() == {
"count": 0,
"invalid_translation_count": 1,
"badge_update": {},
}

translation_dtd_invalid_unapproved.refresh_from_db()
Expand All @@ -195,6 +197,7 @@ def test_batch_find_and_replace_valid_translations(
assert response.json() == {
"count": 1,
"invalid_translation_count": 0,
"badge_update": {},
}

translation = translation_dtd_unapproved.entity.translation_set.last()
Expand Down Expand Up @@ -224,6 +227,7 @@ def test_batch_find_and_replace_invalid_translations(
assert response.json() == {
"count": 0,
"invalid_translation_count": 1,
"badge_update": {},
}

translation = translation_dtd_unapproved.entity.translation_set.last()
Expand Down
7 changes: 6 additions & 1 deletion pontoon/batch/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,11 @@ def batch_edit_translations(request):
invalid_translation_count = len(action_status.get("invalid_translation_pks", []))
if action_status["count"] == 0:
return JsonResponse(
{"count": 0, "invalid_translation_count": invalid_translation_count}
{
"count": 0,
"invalid_translation_count": invalid_translation_count,
"badge_update": action_status["badge_update"],
}
)

tr_pks = [tr.pk for tr in action_status["translated_resources"]]
Expand Down Expand Up @@ -145,5 +149,6 @@ def batch_edit_translations(request):
{
"count": action_status["count"],
"invalid_translation_count": invalid_translation_count,
"badge_update": action_status["badge_update"],
}
)
6 changes: 3 additions & 3 deletions pontoon/translations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def create_translation(request):
# Send Translation Champion Badge notification information
translation_count = user.badges_translation_count
if translation_count in settings.BADGES_TRANSLATION_THRESHOLDS:
badge_name = "Translation Champion Badge"
badge_name = "Translation Champion"
badge_level = (
settings.BADGES_TRANSLATION_THRESHOLDS.index(translation_count) + 1
)
Expand Down Expand Up @@ -323,7 +323,7 @@ def approve_translation(request):
# Send Review Master Badge notification information
review_count = user.badges_review_count
if review_count in settings.BADGES_REVIEW_THRESHOLDS:
badge_name = "Review Master Badge"
badge_name = "Review Master"
badge_level = settings.BADGES_REVIEW_THRESHOLDS.index(review_count) + 1
_add_badge_data(response_data, user, badge_name, badge_level)

Expand Down Expand Up @@ -460,7 +460,7 @@ def reject_translation(request):
# Send Review Master Badge notification information
review_count = request.user.badges_review_count
if review_count in settings.BADGES_REVIEW_THRESHOLDS:
badge_name = "Review Master Badge"
badge_name = "Review Master"
badge_level = settings.BADGES_REVIEW_THRESHOLDS.index(review_count) + 1
_add_badge_data(response_data, request.user, badge_name, badge_level)

Expand Down
7 changes: 6 additions & 1 deletion translate/src/api/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
EntityTranslation,
HistoryTranslation,
} from './translation';
import type { BatchBadgeUpdate } from '../modules/batchactions/actions';

/**
* String that needs to be translated, along with its current metadata,
Expand Down Expand Up @@ -42,7 +43,11 @@ export type EntitySiblings = {
};

type BatchEditResponse =
| { count: number; invalid_translation_count?: number }
| {
count: number;
invalid_translation_count?: number;
badge_update?: BatchBadgeUpdate;
}
| { error: true };

export async function batchEditEntities(
Expand Down
22 changes: 22 additions & 0 deletions translate/src/modules/batchactions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ export const RESET_BATCHACTIONS_RESPONSE = 'batchactions/RESET_RESPONSE';
export const TOGGLE_BATCHACTIONS = 'batchactions/TOGGLE';
export const UNCHECK_BATCHACTIONS = 'batchactions/UNCHECK';

export type BatchBadgeUpdate = {
name: string | null;
level: number | null;
};

export type ResponseType = {
action: string;
changedCount: number | null | undefined;
invalidCount: number | null | undefined;
badgeUpdate: BatchBadgeUpdate | null | undefined;
error: boolean | null | undefined;
};

Expand Down Expand Up @@ -117,6 +123,10 @@ export const performAction =
location: Location,
action: 'approve' | 'reject' | 'replace',
entityIds: number[],
showBadgeTooltip: (tooltip: {
badgeName: string | null;
badgeLevel: number | null;
}) => void,
find?: string,
replace?: string,
) =>
Expand All @@ -134,13 +144,25 @@ export const performAction =
const response: ResponseType = {
changedCount: 0,
invalidCount: 0,
badgeUpdate: {
name: '',
level: 0,
},
error: false,
action,
};

if ('count' in data) {
response.changedCount = data.count;
response.invalidCount = data.invalid_translation_count;
response.badgeUpdate = data.badge_update;

if (response.badgeUpdate?.level && response.badgeUpdate?.level > 0) {
showBadgeTooltip({
badgeName: response.badgeUpdate.name,
badgeLevel: response.badgeUpdate.level,
});
}

if (data.count > 0) {
dispatch(updateUI(location, entityIds));
Expand Down
21 changes: 19 additions & 2 deletions translate/src/modules/batchactions/components/BatchActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Localized } from '@fluent/react';
import React, { useCallback, useContext, useEffect, useRef } from 'react';

import { Location } from '~/context/Location';
import { ShowBadgeTooltip } from '~/context/BadgeTooltip';
import { useAppDispatch, useAppSelector } from '~/hooks';

import { performAction, resetSelection, selectAll } from '../actions';
Expand All @@ -18,6 +19,7 @@ import { ReplaceAll } from './ReplaceAll';
export function BatchActions(): React.ReactElement<'div'> {
const batchactions = useAppSelector((state) => state[BATCHACTIONS]);
const location = useContext(Location);
const showBadgeTooltip = useContext(ShowBadgeTooltip);
const dispatch = useAppDispatch();

const find = useRef<HTMLInputElement>(null);
Expand All @@ -43,13 +45,27 @@ export function BatchActions(): React.ReactElement<'div'> {

const approveAll = useCallback(() => {
if (!batchactions.requestInProgress) {
dispatch(performAction(location, 'approve', batchactions.entities));
dispatch(
performAction(
location,
'approve',
batchactions.entities,
showBadgeTooltip,
),
);
}
}, [location, batchactions]);

const rejectAll = useCallback(() => {
if (!batchactions.requestInProgress) {
dispatch(performAction(location, 'reject', batchactions.entities));
dispatch(
performAction(
location,
'reject',
batchactions.entities,
showBadgeTooltip,
),
);
}
}, [location, batchactions]);

Expand All @@ -67,6 +83,7 @@ export function BatchActions(): React.ReactElement<'div'> {
location,
'replace',
batchactions.entities,
showBadgeTooltip,
encodeURIComponent(fv),
encodeURIComponent(rv),
),
Expand Down
5 changes: 3 additions & 2 deletions translate/src/modules/editor/hooks/useSendTranslation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,10 @@ export function useSendTranslation(): (ignoreWarnings?: boolean) => void {
);

const badgeLevel = content.badge_update?.level;
if (badgeLevel) {
const badgeName = content.badge_update?.name;
if (badgeName && badgeLevel) {
showBadgeTooltip({
badgeName: 'Translation Champion',
badgeName: badgeName,
badgeLevel: badgeLevel,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ export function useUpdateTranslationStatus(

// Check for update in badge level
const badgeLevel = results.badge_update?.level;
if (badgeLevel) {
const badgeName = results.badge_update?.name;
if (badgeName && badgeLevel) {
showBadgeTooltip({
badgeName: 'Review Master',
badgeName: badgeName,
badgeLevel: badgeLevel,
});
}
Expand Down
Loading