Coverage for app/venv/lib/python3.14/site-packages/weblate/trans/views/source.py: 22%

89 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 

4from __future__ import annotations 

5 

6from typing import TYPE_CHECKING 

7 

8from django.contrib.auth.decorators import login_required 

9from django.core.exceptions import PermissionDenied 

10from django.db import transaction 

11from django.http import Http404 

12from django.http.response import HttpResponseServerError 

13from django.shortcuts import get_object_or_404 

14from django.utils.translation import gettext 

15from django.views.decorators.http import require_POST 

16 

17from weblate.checks.flags import Flags 

18from weblate.trans.forms import ContextForm, MatrixLanguageForm 

19from weblate.trans.models import Component, Unit 

20from weblate.trans.util import redirect_next, render 

21from weblate.utils import messages 

22from weblate.utils.views import parse_path, show_form_errors 

23 

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

25 from weblate.auth.models import AuthenticatedHttpRequest 

26 

27 

28@require_POST 

29@login_required 

30@transaction.atomic 

31def edit_context(request: AuthenticatedHttpRequest, pk): 

32 unit = get_object_or_404(Unit, pk=pk) 

33 if not unit.is_source and not unit.translation.component.is_glossary: 

34 msg = "Non source unit!" 

35 raise Http404(msg) 

36 

37 do_add = "addflag" in request.POST 

38 if do_add or "removeflag" in request.POST: 

39 if not request.user.has_perm("meta:unit.flag", unit.translation): 

40 raise PermissionDenied 

41 flag = request.POST.get("addflag", request.POST.get("removeflag")) 

42 flags = unit.get_unit_flags() 

43 if ( 

44 flag in {"terminology", "forbidden", "read-only"} 

45 and not unit.is_source 

46 and flag not in flags 

47 ): 

48 unit = unit.source_unit 

49 flags = Flags(unit.extra_flags) 

50 if do_add: 

51 flags.merge(flag) 

52 else: 

53 flags.remove(flag) 

54 new_flags = flags.format() 

55 if new_flags != unit.extra_flags: 

56 unit.update_extra_flags(new_flags, request.user) 

57 else: 

58 if not request.user.has_perm("source.edit", unit.translation): 

59 raise PermissionDenied 

60 

61 form = ContextForm(request.POST, instance=unit, user=request.user) 

62 

63 if form.is_valid(): 

64 form.save() 

65 else: 

66 messages.error(request, gettext("Could not change additional string info!")) 

67 show_form_errors(request, form) 

68 

69 return redirect_next(request.POST.get("next"), unit.get_absolute_url()) 

70 

71 

72@login_required 

73def matrix(request: AuthenticatedHttpRequest, path): 

74 """Matrix view of all strings.""" 

75 obj = parse_path(request, path, (Component,)) 

76 

77 show = False 

78 translations = None 

79 language_codes_url = None 

80 

81 if "lang" in request.GET: 

82 form = MatrixLanguageForm(obj, request.GET) 

83 show = form.is_valid() 

84 else: 

85 form = MatrixLanguageForm(obj) 

86 

87 if show: 

88 translations = ( 

89 obj.translation_set.filter(language__code__in=form.cleaned_data["lang"]) 

90 .select_related("language") 

91 .order() 

92 ) 

93 language_codes_url = "&".join( 

94 f"lang={translation.language.code}" for translation in translations 

95 ) 

96 

97 return render( 

98 request, 

99 "matrix.html", 

100 { 

101 "object": obj, 

102 "project": obj.project, 

103 "component": obj, 

104 "translations": translations, 

105 "language_codes_url": language_codes_url, 

106 "languages_form": form, 

107 }, 

108 ) 

109 

110 

111@login_required 

112def matrix_load(request: AuthenticatedHttpRequest, path): 

113 """Backend for matrix view of all strings.""" 

114 obj = parse_path(request, path, (Component,)) 

115 

116 try: 

117 offset = int(request.GET.get("offset", "")) 

118 except ValueError: 

119 return HttpResponseServerError("Missing offset") 

120 form = MatrixLanguageForm(obj, request.GET) 

121 if not form.is_valid(): 

122 return HttpResponseServerError("Missing lang") 

123 language_codes = form.cleaned_data["lang"] 

124 

125 # Can not use filter to keep ordering 

126 translations = [ 

127 get_object_or_404(obj.translation_set, language__code=lang) 

128 for lang in language_codes 

129 ] 

130 

131 data = [] 

132 

133 source_units = obj.source_translation.unit_set.order()[offset : offset + 20] 

134 source_ids = [unit.pk for unit in source_units] 

135 

136 translated_units = [ 

137 { 

138 unit.source_unit_id: unit 

139 for unit in translation.unit_set.order().filter(source_unit__in=source_ids) 

140 } 

141 for translation in translations 

142 ] 

143 

144 for unit in source_units: 

145 units = [] 

146 # Avoid need to fetch source unit again 

147 unit.source_unit = unit 

148 for translation in translated_units: 

149 if unit.pk in translation: 

150 # Avoid need to fetch source unit again 

151 translation[unit.pk].source_unit = unit 

152 units.append(translation[unit.pk]) 

153 else: 

154 units.append(None) 

155 

156 data.append((unit, units)) 

157 

158 return render( 

159 request, 

160 "matrix-table.html", 

161 { 

162 "object": obj, 

163 "data": data, 

164 "last": translations[0].unit_set.count() <= offset + 20, 

165 }, 

166 )