Coverage for app/venv/lib/python3.14/site-packages/weblate/glossary/forms.py: 33%

57 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 07:15 +0000

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

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

4 

5from __future__ import annotations 

6 

7from typing import TYPE_CHECKING, cast 

8 

9from django import forms 

10from django.core.exceptions import ValidationError 

11from django.utils.translation import gettext 

12 

13from weblate.trans.forms import NewBilingualGlossaryUnitForm 

14from weblate.trans.models import Translation, Unit 

15 

16if TYPE_CHECKING: 16 ↛ 17line 16 didn't jump to line 17 because the condition on line 16 was never true

17 from weblate.auth.models import User 

18 from weblate.trans.models.translation import NewUnitParams 

19 

20 

21class CommaSeparatedIntegerField(forms.Field): 

22 def to_python(self, value) -> list[int]: 

23 if not value: 

24 return [] 

25 

26 try: 

27 return [int(item.strip()) for item in value.split(",") if item.strip()] 

28 except (ValueError, TypeError) as error: 

29 raise ValidationError(gettext("Invalid integer list!")) from error 

30 

31 

32class GlossaryModelChoiceField(forms.ModelChoiceField): 

33 def label_from_instance(self, obj) -> str: 

34 return obj.component.name 

35 

36 

37class TermForm(NewBilingualGlossaryUnitForm, forms.ModelForm): 

38 """Form for adding term to a glossary.""" 

39 

40 terms = CommaSeparatedIntegerField(widget=forms.HiddenInput, required=False) 

41 

42 class Meta: 

43 model = Unit 

44 fields = [ # noqa: RUF012 

45 "context", 

46 "source", 

47 "target", 

48 "translation", 

49 "explanation", 

50 ] 

51 widgets = { # noqa: RUF012 

52 "context": forms.TextInput, 

53 "source": forms.TextInput, 

54 "target": forms.TextInput, 

55 "explanation": forms.TextInput, 

56 } 

57 field_classes = { # noqa: RUF012 

58 "translation": GlossaryModelChoiceField, 

59 } 

60 

61 def __init__(self, unit: Unit, user: User, data: dict | None = None) -> None: 

62 self.unit = unit 

63 translation = unit.translation 

64 self.glossaries = Translation.objects.filter( 

65 language=translation.language, 

66 component__in=translation.component.project.glossaries, 

67 component__manage_units=True, 

68 ).prefetch() 

69 exclude = [ 

70 glossary.pk 

71 for glossary in self.glossaries 

72 if not user.has_perm("unit.add", glossary) 

73 ] 

74 if exclude: 

75 self.glossaries = self.glossaries.exclude(pk__in=exclude) 

76 

77 super().__init__( 

78 translation=translation, 

79 user=user, 

80 data=data, 

81 auto_id="id_add_term_%s", 

82 ) 

83 

84 def patch_fields(self) -> None: 

85 super().patch_fields() 

86 self.fields["translation"].queryset = self.glossaries 

87 self.fields["translation"].label = gettext("Glossary") 

88 if len(self.glossaries) == 1: 

89 self.fields["translation"].initial = self.glossaries[0] 

90 

91 if self.unit.is_source: 

92 self.fields["terminology"].initial = True 

93 

94 if self.unit.translation.is_source: 

95 self.fields["target"].required = False 

96 self.fields["target"].widget = forms.HiddenInput() 

97 

98 def clean(self) -> None: 

99 self.translation = cast("Translation", self.cleaned_data.get("translation")) 

100 # Validate fields only if translation is valid 

101 if self.translation: 

102 super().clean() 

103 

104 def as_kwargs(self) -> NewUnitParams: 

105 result = super().as_kwargs() 

106 if self.cleaned_data["translation"].is_source: 

107 result["target"] = result["source"] 

108 result["skip_existing"] = bool(self.cleaned_data.get("terminology")) 

109 return result