This commit is contained in:
Harmit Goswami 2025-01-15 15:19:29 -05:00 коммит произвёл GitHub
Родитель 402bf74812
Коммит 89f390a551
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 92 добавлений и 29 удалений

Просмотреть файл

@ -1,3 +1,4 @@
import json
import logging
import re
@ -805,8 +806,18 @@ def upload(request):
upload = request.FILES["uploadfile"]
try:
import_uploaded_file(project, locale, res_path, upload, request.user)
badge_name, badge_level = import_uploaded_file(
project, locale, res_path, upload, request.user
)
messages.success(request, "Translations updated from uploaded file.")
if badge_name:
message = json.dumps(
{
"name": badge_name,
"level": badge_level,
}
)
messages.info(request, message, extra_tags="badge")
except Exception as error:
messages.error(request, str(error))
else:

Просмотреть файл

@ -29,6 +29,7 @@ from pontoon.base.models import (
)
from pontoon.checks import DB_FORMATS
from pontoon.checks.utils import bulk_run_checks
from pontoon.messaging.notifications import send_badge_notification
from pontoon.sync.core.checkout import Checkout, Checkouts
from pontoon.sync.core.paths import UploadPaths
from pontoon.sync.formats import parse
@ -76,12 +77,13 @@ def sync_translations_from_repo(
def write_db_updates(
project: Project, updates: Updates, user: User | None, now: datetime
) -> None:
updated_translations, new_translations = update_db_translations(
project, updates, user, now
) -> tuple[str, int]:
badge_name, badge_level, updated_translations, new_translations = (
update_db_translations(project, updates, user, now)
)
add_failed_checks(new_translations)
add_translation_memory_entries(project, new_translations + updated_translations)
return badge_name, badge_level
def delete_removed_bilingual_resources(
@ -460,10 +462,24 @@ def update_db_translations(
f"[{project.slug}] Created {str_n_translations(created)} from repo changes"
)
badge_name = ""
badge_level = 0
if actions:
ActionLog.objects.bulk_create(actions)
translation_before_level = log_user.badges_translation_level
review_before_level = log_user.badges_review_level
return created, list(suggestions.values())
ActionLog.objects.bulk_create(actions)
if log_user.username != "pontoon-sync":
if log_user.badges_translation_level > translation_before_level:
badge_name = "Translation Champion"
badge_level = log_user.badges_translation_level
send_badge_notification(log_user, badge_name, badge_level)
if log_user.badges_review_level > review_before_level:
badge_name = "Review Master"
badge_level = log_user.badges_review_level
send_badge_notification(log_user, badge_name, badge_level)
return badge_name, badge_level, created, list(suggestions.values())
def str_n_translations(n: int | Sized) -> str:

Просмотреть файл

@ -96,7 +96,7 @@ def import_uploaded_file(
)
if updates:
now = timezone.now()
write_db_updates(project, updates, user, now)
badge_name, badge_level = write_db_updates(project, updates, user, now)
update_stats(project)
ChangedEntityLocale.objects.bulk_create(
(
@ -105,5 +105,6 @@ def import_uploaded_file(
),
ignore_conflicts=True,
)
return badge_name, badge_level
else:
raise Exception("Upload failed.")

Просмотреть файл

@ -1,5 +1,5 @@
import { createContext, useEffect, useState } from 'react';
import { Localized } from '@fluent/react';
import { createContext } from 'react';
import { useNotifications } from '~/hooks/useNotifications';
export type BadgeTooltipMessage = Readonly<{
badgeName: string | null;
@ -19,11 +19,11 @@ export function BadgeTooltipProvider({
}: {
children: React.ReactElement;
}) {
const [message, setMessage] = useState<BadgeTooltipMessage | null>(null);
const { badgeMessage, setBadgeMessage } = useNotifications();
return (
<BadgeTooltipMessage.Provider value={message}>
<ShowBadgeTooltip.Provider value={(tooltip) => setMessage(tooltip)}>
<BadgeTooltipMessage.Provider value={badgeMessage}>
<ShowBadgeTooltip.Provider value={(tooltip) => setBadgeMessage(tooltip)}>
{children}
</ShowBadgeTooltip.Provider>
</BadgeTooltipMessage.Provider>

Просмотреть файл

@ -1,4 +1,5 @@
import { createContext, useEffect, useState } from 'react';
import { createContext } from 'react';
import { useNotifications } from '~/hooks/useNotifications';
type NotificationType = 'debug' | 'error' | 'info' | 'success' | 'warning';
@ -20,22 +21,7 @@ export function NotificationProvider({
}: {
children: React.ReactElement;
}) {
const [message, setMessage] = useState<NotificationMessage | null>(null);
// If there's a notification in the DOM set by Django, show it.
// Note that we only show it once, and only when the UI has already
// been rendered, to make sure users do see it.
useEffect(() => {
const rootElt = document.getElementById('root');
if (rootElt?.dataset.notifications) {
const notifications = JSON.parse(rootElt.dataset.notifications);
if (notifications.length > 0) {
// Our notification system only supports showing one notification
// for the moment, so we only add the first notification here.
setMessage(notifications[0]);
}
}
}, []);
const { message, setMessage } = useNotifications();
return (
<NotificationMessage.Provider value={message}>

Просмотреть файл

@ -0,0 +1,49 @@
import { useState, useEffect } from 'react';
import { NotificationMessage } from '~/context/Notification';
import { BadgeTooltipMessage } from '~/context/BadgeTooltip';
export function useNotifications() {
const [message, setMessage] = useState<NotificationMessage | null>(null);
const [badgeMessage, setBadgeMessage] = useState<BadgeTooltipMessage | null>(
null,
);
// If there's a notification in the DOM set by Django, show it.
// Note that we only show it once, and only when the UI has already
// been rendered, to make sure users do see it.
useEffect(() => {
const rootElt = document.getElementById('root');
if (rootElt?.dataset.notifications) {
const notifications = JSON.parse(rootElt.dataset.notifications);
if (notifications.length > 0) {
// Extra tags from the Django messages framework are combined
// with the level into a single string as notification.type
const generalNotification = notifications.find(
(notification: { type: string }) =>
notification.type !== 'badge info',
);
const badgeNotification = notifications.find(
(notification: { type: string }) =>
notification.type === 'badge info',
);
if (generalNotification) {
setMessage({
type: generalNotification.type,
content: generalNotification.content,
});
}
if (badgeNotification) {
const badgeData = JSON.parse(badgeNotification.content);
setBadgeMessage({
badgeName: badgeData.name || null,
badgeLevel: badgeData.level || null,
});
}
}
}
}, []);
return { message, setMessage, badgeMessage, setBadgeMessage };
}