Skip to content

Commit 595d043

Browse files
committed
chore(autotranslate): code cleanups
- avoid using kwargs for class parameters, instead pass them directly - directly filter the queryset instead of building filters and applying them later - improved error handling in the form
1 parent 20f334f commit 595d043

File tree

4 files changed

+76
-47
lines changed

4 files changed

+76
-47
lines changed

weblate/trans/autotranslate.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
from django.db.models.functions import MD5, Lower
1515
from django.utils.translation import gettext, ngettext
1616

17-
from weblate.machinery.base import (
18-
MachineTranslationError,
19-
)
17+
from weblate.machinery.base import MachineTranslationError
2018
from weblate.machinery.models import MACHINERY
2119
from weblate.trans.actions import ActionEvents
2220
from weblate.trans.models import Category, Component, Suggestion, Translation, Unit
@@ -30,16 +28,11 @@
3028
from weblate.utils.stats import ProjectLanguage
3129

3230
if TYPE_CHECKING:
33-
from collections.abc import Iterable
31+
from collections.abc import Sequence
3432

3533
from weblate.auth.models import User
36-
from weblate.machinery.base import (
37-
BatchMachineTranslation,
38-
UnitMemoryResultDict,
39-
)
40-
from weblate.utils.state import (
41-
StringState,
42-
)
34+
from weblate.machinery.base import BatchMachineTranslation, UnitMemoryResultDict
35+
from weblate.utils.state import StringState
4336

4437

4538
class BaseAutoTranslate:
@@ -54,7 +47,6 @@ def __init__(
5447
mode: str,
5548
component_wide: bool = False,
5649
unit_ids: list[int] | None = None,
57-
**kwargs,
5850
) -> None:
5951
self.user: User | None = user
6052
self.q: str = q
@@ -89,8 +81,19 @@ def set_progress(self, current: int) -> None:
8981

9082

9183
class AutoTranslate(BaseAutoTranslate):
92-
def __init__(self, *, translation: Translation, **kwargs) -> None:
93-
super().__init__(**kwargs)
84+
def __init__(
85+
self,
86+
*,
87+
translation: Translation,
88+
user: User | None,
89+
q: str,
90+
mode: str,
91+
component_wide: bool = False,
92+
unit_ids: list[int] | None = None,
93+
) -> None:
94+
super().__init__(
95+
user=user, q=q, mode=mode, component_wide=component_wide, unit_ids=unit_ids
96+
)
9497
self.translation: Translation = translation
9598
translation.component.start_batched_checks()
9699
self.progress_base = 0
@@ -151,12 +154,11 @@ def post_process(self) -> None:
151154
@transaction.atomic
152155
def process_others(self, source_component_id: int | None) -> None:
153156
"""Perform automatic translation based on other components."""
154-
kwargs = {
155-
"translation__plural": self.translation.plural,
156-
"state__gte": STATE_TRANSLATED,
157-
}
157+
sources = Unit.objects.filter(
158+
translation__plural=self.translation.plural,
159+
state__gte=STATE_TRANSLATED,
160+
)
158161
source_language = self.translation.component.source_language
159-
exclude = {}
160162
if source_component_id:
161163
component = Component.objects.get(id=source_component_id)
162164

@@ -169,23 +171,20 @@ def process_others(self, source_component_id: int | None) -> None:
169171
if component.source_language != source_language:
170172
msg = "Component have different source languages."
171173
raise PermissionDenied(msg)
172-
kwargs["translation__component"] = component
174+
sources = sources.filter(translation__component=component)
173175
else:
174176
project = self.translation.component.project
175-
kwargs["translation__component__project"] = project
176-
kwargs["translation__component__source_language"] = source_language
177-
exclude["translation"] = self.translation
178-
sources = Unit.objects.filter(**kwargs)
177+
sources = sources.filter(
178+
translation__component__project=project,
179+
translation__component__source_language=source_language,
180+
).exclude(translation=self.translation)
179181

180182
# Use memory_db for the query in case it exists. This is supposed
181183
# to be a read-only replica for offloading expensive translation
182184
# queries.
183185
if "memory_db" in settings.DATABASES:
184186
sources = sources.using("memory_db")
185187

186-
if exclude:
187-
sources = sources.exclude(**exclude)
188-
189188
# Get source MD5s
190189
source_md5s = list(
191190
self.get_units()
@@ -356,7 +355,7 @@ def perform(
356355

357356

358357
class BatchAutoTranslate(BaseAutoTranslate):
359-
translations: Iterable[Translation]
358+
translations: Sequence[Translation]
360359

361360
def __init__(
362361
self,

weblate/trans/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ def clean_component(self):
11131113
raise ValidationError(gettext("Component not found!"))
11141114
try:
11151115
result = self.components.get(slug=component, project=self.project)
1116-
except Component.DoesNotExist as error:
1116+
except (Component.DoesNotExist, Component.MultipleObjectsReturned) as error:
11171117
raise ValidationError(gettext("Component not found!")) from error
11181118
else:
11191119
try:

weblate/trans/tasks.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717
from django.conf import settings
1818
from django.contrib.staticfiles.storage import staticfiles_storage
1919
from django.core.cache import cache
20+
from django.core.exceptions import PermissionDenied
2021
from django.db import IntegrityError, transaction
21-
from django.db.models import (
22-
Count,
23-
F,
24-
)
22+
from django.db.models import Count, F
2523
from django.http import Http404
2624
from django.utils import timezone
2725
from django.utils.timezone import make_aware
@@ -572,13 +570,17 @@ def auto_translate( # noqa: PLR0913
572570
component_wide=component_wide,
573571
unit_ids=unit_ids,
574572
)
575-
message = auto.perform(
576-
auto_source=auto_source,
577-
engines=engines,
578-
threshold=threshold,
579-
source_component_id=source_component_id,
580-
)
581-
result.update({"message": message})
573+
try:
574+
message = auto.perform(
575+
auto_source=auto_source,
576+
engines=engines,
577+
threshold=threshold,
578+
source_component_id=source_component_id,
579+
)
580+
except PermissionDenied as error:
581+
result.update({"message": str(error)})
582+
else:
583+
result.update({"message": message})
582584
return result
583585

584586

weblate/trans/tests/test_autotranslate.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,30 @@
1111

1212
from weblate.lang.models import Language
1313
from weblate.trans.actions import ActionEvents
14-
from weblate.trans.models import Change, Component, PendingUnitChange
14+
from weblate.trans.models import Change, Component, PendingUnitChange, Project
1515
from weblate.trans.tasks import auto_translate
1616
from weblate.trans.tests.test_views import ViewTestCase
1717
from weblate.utils.stats import ProjectLanguage
1818

1919

2020
class AutoTranslationTest(ViewTestCase):
21+
use_component_id: bool = False
22+
2123
def setUp(self) -> None:
2224
super().setUp()
2325
# Need extra power
2426
self.user.is_superuser = True
2527
self.user.save()
2628
self.project.translation_review = True
2729
self.project.save()
30+
self.component2 = self.create_second_component()
31+
32+
def create_second_component(self, project: Project | None = None) -> Component:
2833
with override_settings(CREATE_GLOSSARIES=self.CREATE_GLOSSARIES):
29-
self.component2 = Component.objects.create(
34+
return Component.objects.create(
3035
name="Test 2",
3136
slug="test-2",
32-
project=self.project,
37+
project=self.project if project is None else project,
3338
repo=self.git_repo_path,
3439
push=self.git_repo_path,
3540
vcs="git",
@@ -63,6 +68,8 @@ def perform_auto(
6368
kwargs["q"] = "state:<translated"
6469
if "mode" not in kwargs:
6570
kwargs["mode"] = "translate"
71+
if self.use_component_id:
72+
kwargs["component"] = self.component.id
6673
response = self.client.post(url, kwargs, follow=True)
6774
if expected == 0:
6875
expected_string = (
@@ -128,11 +135,16 @@ def test_autotranslate_component(self) -> None:
128135
self.assertEqual(de_translation.stats.translated, initial_stats + 1)
129136

130137
def test_autotranslate_category(self) -> None:
131-
category = self.create_category(project=self.project)
132-
self.component.category = self.component2.category = category
138+
self.component.category = self.create_category(project=self.project)
139+
category = self.component.category
140+
if self.component2.project != self.project:
141+
category = self.create_category(project=self.component2.project)
142+
self.component2.category = category
133143
self.component.save()
134144
self.component2.save()
145+
135146
self.make_different("de")
147+
136148
self.perform_auto(
137149
path_params={"path": category.get_url_path()},
138150
expected=2,
@@ -141,7 +153,7 @@ def test_autotranslate_category(self) -> None:
141153

142154
def test_autotranslate_project_language(self) -> None:
143155
project_language = ProjectLanguage(
144-
self.project,
156+
self.component2.project,
145157
language=Language.objects.get(code="cs"),
146158
)
147159
self.make_different("de")
@@ -307,7 +319,13 @@ def test_command_add(self) -> None:
307319

308320
def test_command_different(self) -> None:
309321
self.make_different()
310-
call_command("auto_translate", "test", "test-2", "cs", source="test/test")
322+
call_command(
323+
"auto_translate",
324+
self.component2.project.slug,
325+
self.component2.slug,
326+
"cs",
327+
source=self.component.full_slug,
328+
)
311329

312330
def test_command_errors(self) -> None:
313331
with self.assertRaises(CommandError):
@@ -320,6 +338,16 @@ def test_command_errors(self) -> None:
320338
call_command("auto_translate", "test", "test", "xxx")
321339

322340

341+
class AutoTranslationCrossProjectTest(AutoTranslationTest):
342+
use_component_id: bool = True
343+
344+
def create_second_component(self, project: Project | None = None) -> Component:
345+
project = Project.objects.create(
346+
name="Other", slug="other", translation_review=True
347+
)
348+
return super().create_second_component(project=project)
349+
350+
323351
class AutoTranslationMtTest(ViewTestCase):
324352
def setUp(self) -> None:
325353
super().setUp()

0 commit comments

Comments
 (0)