Skip to content

Commit

Permalink
feat(notifications): Added translation notifications (#11712)
Browse files Browse the repository at this point in the history
Added a new notification for:
- when all the languages in component are translated.
- when a language is translated.
* fix(template): applied changes in translated_language template
* fix(testsuite): updated notification subscription count
* fix(test_notifications): Updated notify_language_translated to use same scope as global
* fix(tasks): refactored code and reverted relocation of change to component
* fix(notifications): removed get_context from LanguageTranslatedNotificaton
* fix(testsuite): updated subscription set count in test_watch
* fix(test_notifications): updated to use same scope as real code

Fixes #4294 and #11215

Co-authored-by: Manuel Cruz <manuelmcruz@tecnico.ulisboa.pt>
Co-authored-by: Benjamin Alan Jamie <benjamin@weblate.org>
Co-authored-by: Michal Čihař <michal@cihar.com>
  • Loading branch information
4 people committed Jun 24, 2024
1 parent 1152ebb commit 8462ccc
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright © Michal Čihař <michal@weblate.org>

# SPDX-License-Identifier: GPL-3.0-or-later

# Generated by Django 5.0.6 on 2024-06-19 16:01

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("accounts", "0010_alter_subscription_notification"),
]

operations = [
migrations.AlterField(
model_name="subscription",
name="notification",
field=models.CharField(
choices=[
(
"RepositoryNotification",
"Operation was performed in the repository",
),
("LockNotification", "Component was locked or unlocked"),
("LicenseNotification", "License was changed"),
("ParseErrorNotification", "Parse error occurred"),
("NewStringNotificaton", "String is available for translation"),
(
"NewContributorNotificaton",
"Contributor made their first translation",
),
("NewSuggestionNotificaton", "Suggestion was added"),
("LanguageTranslatedNotificaton", "Language was translated"),
("ComponentTranslatedNotificaton", "Component was translated"),
("NewCommentNotificaton", "Comment was added"),
("MentionCommentNotificaton", "You were mentioned in a comment"),
(
"LastAuthorCommentNotificaton",
"Your translation received a comment",
),
("TranslatedStringNotificaton", "String was edited by user"),
("ApprovedStringNotificaton", "String was approved"),
("ChangedStringNotificaton", "String was changed"),
(
"NewTranslationNotificaton",
"New language was added or requested",
),
(
"NewComponentNotificaton",
"New translation component was created",
),
("NewAnnouncementNotificaton", "Announcement was published"),
("NewAlertNotificaton", "New alert emerged in a component"),
("MergeFailureNotification", "Repository operation failed"),
("PendingSuggestionsNotification", "Pending suggestions exist"),
("ToDoStringsNotification", "Unfinished strings exist"),
],
max_length=100,
),
),
]
16 changes: 16 additions & 0 deletions weblate/accounts/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,22 @@ class NewSuggestionNotificaton(Notification):
required_attr = "suggestion"


@register_notification
class LanguageTranslatedNotificaton(Notification):
actions = (Change.ACTION_COMPLETE,)
verbose = pgettext_lazy("Notification name", "Language was translated")
template_name = "translated_language"
required_attr = "translation"


@register_notification
class ComponentTranslatedNotificaton(Notification):
actions = (Change.ACTION_COMPLETED_COMPONENT,)
verbose = pgettext_lazy("Notification name", "Component was translated")
template_name = "translated_component"
required_attr = "component"


@register_notification
class NewCommentNotificaton(Notification):
actions = (Change.ACTION_COMMENT,)
Expand Down
25 changes: 25 additions & 0 deletions weblate/accounts/tests/test_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def setUp(self) -> None:
"NewTranslationNotificaton",
"MentionCommentNotificaton",
"LastAuthorCommentNotificaton",
"ComponentTranslatedNotificaton",
"LanguageTranslatedNotificaton",
)
for notification in notifications:
# Remove any conflicting notifications
Expand Down Expand Up @@ -342,6 +344,29 @@ def test_notify_new_announcement(self) -> None:
"[Weblate] New announcement at Weblate",
)

def test_notify_component_translated(self) -> None:
unit = self.get_unit()
unit.translation.component.change_set.create(
user=self.anotheruser,
old="",
action=Change.ACTION_COMPLETED_COMPONENT,
)

# Check mail - TranslatedComponentNotification
self.validate_notifications(
1,
"[Weblate] Translations in all languages have been completed in Test/Test",
)

def test_notify_language_translated(self) -> None:
unit = self.get_unit(language="cs")
unit.translation.change_set.create(
user=self.anotheruser,
action=Change.ACTION_COMPLETE,
)

self.validate_notifications(1, "[Weblate] Test/Test — Czech has been completed")

def test_notify_alert(self) -> None:
self.component.project.add_user(self.user, "Administration")
self.component.add_alert("PushFailure", error="Some error")
Expand Down
6 changes: 3 additions & 3 deletions weblate/accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,13 +509,13 @@ def test_watch(self) -> None:
# Mute notifications for component
self.client.post(reverse("mute", kwargs=self.kw_component))
self.assertEqual(
self.user.subscription_set.filter(component=self.component).count(), 18
self.user.subscription_set.filter(component=self.component).count(), 20
)

# Mute notifications for project
self.client.post(reverse("mute", kwargs={"path": self.project.get_url_path()}))
self.assertEqual(
self.user.subscription_set.filter(project=self.project).count(), 18
self.user.subscription_set.filter(project=self.project).count(), 20
)

# Unwatch project
Expand All @@ -540,7 +540,7 @@ def test_watch_component(self) -> None:
self.assertEqual(self.user.profile.watched.count(), 1)
# All project notifications should be muted
self.assertEqual(
self.user.subscription_set.filter(project=self.project).count(), 18
self.user.subscription_set.filter(project=self.project).count(), 20
)
# Only default notifications should be enabled
self.assertEqual(
Expand Down
4 changes: 4 additions & 0 deletions weblate/templates/mail/translated_component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "mail/base.html" %}

{% block content %}
{% endblock %}
4 changes: 4 additions & 0 deletions weblate/templates/mail/translated_component_subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Translations in all languages have been completed in {{ component }}{% endblocktrans %}
{% endautoescape %}
4 changes: 4 additions & 0 deletions weblate/templates/mail/translated_language.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "mail/base.html" %}

{% block content %}
{% endblock %}
4 changes: 4 additions & 0 deletions weblate/templates/mail/translated_language_subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}{{translation}} has been completed{% endblocktrans %}
{% endautoescape %}
95 changes: 95 additions & 0 deletions weblate/trans/migrations/0019_alter_change_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright © Michal Čihař <michal@weblate.org>

# SPDX-License-Identifier: GPL-3.0-or-later

# Generated by Django 5.0.6 on 2024-06-19 16:09

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("trans", "0018_merge_20240529_1359"),
]

operations = [
migrations.AlterField(
model_name="change",
name="action",
field=models.IntegerField(
choices=[
(0, "Resource updated"),
(1, "Translation completed"),
(2, "Translation changed"),
(5, "Translation added"),
(3, "Comment added"),
(4, "Suggestion added"),
(6, "Automatically translated"),
(7, "Suggestion accepted"),
(8, "Translation reverted"),
(9, "Translation uploaded"),
(13, "Source string added"),
(14, "Component locked"),
(15, "Component unlocked"),
(17, "Changes committed"),
(18, "Changes pushed"),
(19, "Repository reset"),
(20, "Repository merged"),
(21, "Repository rebased"),
(22, "Repository merge failed"),
(23, "Repository rebase failed"),
(28, "Repository push failed"),
(24, "Parsing failed"),
(25, "Translation removed"),
(26, "Suggestion removed"),
(27, "Translation replaced"),
(29, "Suggestion removed during cleanup"),
(30, "Source string changed"),
(31, "String added"),
(32, "Bulk status changed"),
(33, "Visibility changed"),
(34, "User added"),
(35, "User removed"),
(36, "Translation approved"),
(37, "Marked for edit"),
(38, "Component removed"),
(39, "Project removed"),
(41, "Project renamed"),
(42, "Component renamed"),
(43, "Moved component"),
(45, "Contributor joined"),
(46, "Announcement posted"),
(47, "Alert triggered"),
(48, "Language added"),
(49, "Language requested"),
(50, "Project created"),
(51, "Component created"),
(52, "User invited"),
(53, "Repository notification received"),
(54, "Translation replaced file by upload"),
(55, "License changed"),
(56, "Contributor agreement changed"),
(57, "Screenshot added"),
(58, "Screenshot uploaded"),
(59, "String updated in the repository"),
(60, "Add-on installed"),
(61, "Add-on configuration changed"),
(62, "Add-on uninstalled"),
(63, "String removed"),
(64, "Comment removed"),
(65, "Comment resolved"),
(66, "Explanation updated"),
(67, "Category removed"),
(68, "Category renamed"),
(69, "Category moved"),
(70, "Saving string failed"),
(71, "String added in the repository"),
(72, "String updated in the upload"),
(73, "String added in the upload"),
(74, "Translation updated by source upload"),
(75, "Component translation completed"),
],
default=2,
),
),
]
3 changes: 3 additions & 0 deletions weblate/trans/models/change.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ class Change(models.Model, UserDisplayMixin):
ACTION_STRING_UPLOAD_UPDATE = 72
ACTION_NEW_UNIT_UPLOAD = 73
ACTION_SOURCE_UPLOAD = 74
ACTION_COMPLETED_COMPONENT = 75

ACTION_CHOICES = (
# Translators: Name of event in the history
Expand Down Expand Up @@ -502,6 +503,8 @@ class Change(models.Model, UserDisplayMixin):
(ACTION_NEW_UNIT_UPLOAD, gettext_lazy("String added in the upload")),
# Translators: Name of event in the history
(ACTION_SOURCE_UPLOAD, gettext_lazy("Translation updated by source upload")),
# Translators: Name of event in the history
(ACTION_COMPLETED_COMPONENT, gettext_lazy("Component translation completed")),
)
ACTIONS_DICT = dict(ACTION_CHOICES)
ACTION_STRINGS = {
Expand Down
9 changes: 9 additions & 0 deletions weblate/trans/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,15 @@ def detect_completed_translation(change_id: int, old_translated: int) -> None:
author=change.author,
)

# check if component is fully translated
component = change.translation.component
if component.stats.translated == component.stats.all:
change.translation.component.change_set.create(
action=Change.ACTION_COMPLETED_COMPONENT,
user=change.user,
author=change.author,
)


@app.on_after_finalize.connect
def setup_periodic_tasks(sender, **kwargs) -> None:
Expand Down

0 comments on commit 8462ccc

Please sign in to comment.