Coverage for CIResults/bugs_views.py: 100%

117 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-19 09:20 +0000

1from django.contrib import messages 

2from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin 

3from django.contrib.auth.decorators import permission_required 

4from django.db import transaction 

5from django.shortcuts import render, get_object_or_404 

6from django.http import JsonResponse 

7from django.db.models import Prefetch 

8from django.utils import timezone 

9from django.views.decorators.http import require_http_methods, require_POST 

10from django.views.generic import ListView 

11from django.views.generic.edit import CreateView, UpdateView 

12from django.urls import reverse 

13 

14from collections import defaultdict 

15 

16from .models import BugTracker, Bug 

17from .models import BugComment, ReplicationScript 

18 

19from .sandbox.io import Client 

20from .serializers import ReplicationScriptSerializer 

21from .filtering import QueryCreator 

22 

23from datetime import timedelta 

24import json 

25 

26 

27@require_POST 

28@permission_required('CIResults.change_replicationscript') 

29def replication_script_check(request, **kwargs): 

30 script = request.POST.get("script") 

31 src_tracker_name = request.POST["source_tracker"] 

32 dest_tracker_name = request.POST["destination_tracker"] 

33 

34 src_tracker = BugTracker.objects.get(name=src_tracker_name) 

35 dest_tracker = BugTracker.objects.get(name=dest_tracker_name) 

36 

37 try: 

38 client = Client.get_or_create_instance(script) 

39 except ValueError as e: 

40 return JsonResponse({'status': 'false', 'message': str(e)}, status=500) 

41 except (OSError, IOError): 

42 msg = "Script failed, likely due to illegal syscall made by script." 

43 return JsonResponse({'status': 'false', 'message': msg}, status=500) 

44 

45 try: 

46 p = Prefetch('children', queryset=Bug.objects.filter(tracker=dest_tracker), to_attr="children_bugs") 

47 bug_list = Bug.objects.filter(tracker=src_tracker).prefetch_related(p) 

48 bugs = src_tracker.tracker.tracker_check_replication(bug_list, dest_tracker, script, client, dryrun=True) 

49 

50 except Client.UserFunctionCallError as e: 

51 return JsonResponse({'status': 'false', 'message': e.reason}, status=500) 

52 except ValueError as e: 

53 return JsonResponse({'status': 'false', 'message': str(e)}, status=500) 

54 finally: 

55 client.shutdown() 

56 return JsonResponse({'bugs': bugs}, safe=False) 

57 

58 

59class ReplicationScriptListView(ListView): 

60 model = ReplicationScript 

61 context_object_name = 'script_list' 

62 

63 def get_queryset(self): 

64 return ReplicationScript.objects.all() 

65 

66 

67class ReplicationScriptCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): 

68 model = ReplicationScript 

69 fields = ['name', 'enabled', 'source_tracker', 'destination_tracker', 'script'] 

70 template_name = 'CIResults/replication_script.html' 

71 

72 permission_required = 'CIResults.add_replicationscript' 

73 permission_denied_message = "You don't have the necessary permissions to create a replication script" 

74 

75 def get_success_url(self): 

76 messages.success(self.request, "The script {} has been created".format(self.object)) 

77 return reverse("CIResults-replication-script-list") 

78 

79 def form_valid(self, form): 

80 form.instance.created_by = self.request.user 

81 return super().form_valid(form) 

82 

83 

84class ReplicationScriptEditView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): 

85 model = ReplicationScript 

86 fields = ['name', 'enabled', 'source_tracker', 'destination_tracker', 'script'] 

87 template_name = 'CIResults/replication_script.html' 

88 

89 permission_required = 'CIResults.change_replicationscript' 

90 permission_denied_message = "You don't have the necessary permissions to edit a replication script" 

91 

92 def get_success_url(self): 

93 messages.success(self.request, "The script {} has been edited".format(self.object)) 

94 return reverse("CIResults-replication-script-list") 

95 

96 def get_context_data(self, **kwargs): 

97 context = super().get_context_data(**kwargs) 

98 context['script_history'] = ReplicationScript.objects.get(pk=self.object.pk).script_history 

99 return context 

100 

101 def form_valid(self, form): 

102 original = ReplicationScript.objects.get(pk=self.object.pk) 

103 orig_hist = json.loads(form.instance.script_history) 

104 orig_hist.append(ReplicationScriptSerializer(original).data) 

105 form.instance.script_history = json.dumps(orig_hist) 

106 return super().form_valid(form) 

107 

108 

109def open_bugs(request, **kwargs): 

110 # Parse the user request 

111 user_query = QueryCreator(request, Bug).request_to_query() 

112 

113 # If the user request is empty, just select everything 

114 selected_bugs = Bug.objects.all() if user_query.is_empty else user_query.objects 

115 selected_bugs = selected_bugs.defer('description') 

116 selected_bugs = selected_bugs.prefetch_related('tracker', 'assignee__person', "creator__person", 

117 "issue_set", "children", "parent") 

118 

119 referenced_bugs = set([b.id for b in selected_bugs if len(b.issue_set.all()) > 0]) 

120 all_followed_and_open_bugs = set([b for b in selected_bugs if b.is_open and (b.id in referenced_bugs or 

121 b.component in b.tracker.components_followed_list or not b.tracker.tracker.has_components)]) # noqa 

122 all_followed_and_open_bugs = set(b if b.parent is None else b.parent for b in all_followed_and_open_bugs) 

123 

124 all_comments = BugComment.objects.filter(bug__in=selected_bugs).only('bug', 'account', 'created_on') 

125 all_comments = all_comments.prefetch_related('bug__tracker', "account__person") 

126 

127 # HACK: Preload all the comments and assign them to their respective bugs 

128 comments = defaultdict(list) 

129 for comment in all_comments: 

130 comments[(comment.bug_id, comment.bug.tracker_id)].append(comment) 

131 for bug in all_followed_and_open_bugs: 

132 bug.comments_cached = comments[(bug.id, bug.tracker_id)] 

133 

134 # Now, classify the urgency 

135 overdue = list() 

136 close = list() 

137 other = list() 

138 for bug in sorted(all_followed_and_open_bugs, key=lambda b: b.effective_priority, reverse=True): 

139 time_left = bug.SLA_remaining_time 

140 if time_left < timedelta(seconds=0): 

141 overdue.append(bug) 

142 elif time_left < timedelta(days=7): 

143 close.append(bug) # pragma: no cover (TODO: requires to control the current time) 

144 else: 

145 other.insert(-1, bug) 

146 

147 context = { 

148 "trackers": BugTracker.objects.all(), 

149 "all_open_bugs": all_followed_and_open_bugs, 

150 "bugs_overdue": overdue, 

151 "bugs_close_to_deadline": close, 

152 "bugs_other": other, 

153 

154 'filters_model': Bug, 

155 'query': user_query, 

156 } 

157 return render(request, 'CIResults/open_bugs.html', context) 

158 

159 

160@transaction.atomic 

161@require_http_methods(["POST"]) 

162def bug_flag_for_update(request, pk): 

163 bug = get_object_or_404(Bug.objects.select_for_update(), pk=pk) 

164 

165 if not bug.is_being_updated: 

166 bug.flagged_as_update_pending_on = timezone.now() 

167 bug.save() 

168 status = 200 

169 else: 

170 status = 409 

171 

172 return JsonResponse(data={}, status=status)