~vcs-imports/reviewboard/trunk

« back to all changes in this revision

Viewing changes to reviewboard/reviews/views/batch.py

  • Committer: Christian Hammond
  • Date: 2023-11-07 06:16:03 UTC
  • mfrom: (4947.1.350)
  • Revision ID: git-v1:00c845582f57ac3df206a66fe9603a1958b87fb9
Merge branch 'release-6.x'

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
    6.0
5
5
"""
6
6
 
7
 
import copy
8
7
import json
9
8
import logging
10
9
from enum import Enum
107
106
        """
108
107
        data = request.POST
109
108
        batch_str = data.get('batch')
110
 
        user = copy.copy(request.user)
 
109
        user = request.user
 
110
        assert isinstance(user, User)
111
111
 
112
112
        if not batch_str:
113
113
            return JsonResponse(
144
144
 
145
145
        review_request_ids = batch_data.get('review_requests')
146
146
 
147
 
        if not (isinstance(review_request_ids, list) and
148
 
                all(isinstance(id, int) for id in review_request_ids)):
149
 
            return JsonResponse(
150
 
                data={
151
 
                    'stat': 'fail',
152
 
                    'error': 'review_requests must be an array of integers.',
153
 
                },
154
 
                status=400)
155
 
 
156
 
        if self.local_site:
157
 
            id_field = 'local_id'
158
 
            extra_query = Q(local_id__in=review_request_ids)
159
 
        else:
160
 
            id_field = 'pk'
161
 
            extra_query = Q(pk__in=review_request_ids)
162
 
 
163
 
        review_requests_qs = (
164
 
            ReviewRequest.objects.public(
165
 
                user=user,
166
 
                status=None,
167
 
                show_all_unpublished=True,
168
 
                local_site=self.local_site,
169
 
                extra_query=extra_query)
170
 
        )
171
 
 
172
 
        if op == BatchOperation.PUBLISH:
173
 
            review_requests_qs = review_requests_qs.select_related('draft')
174
 
 
175
 
        review_requests = list(review_requests_qs.order_by('pk'))
176
 
 
177
 
        if len(review_requests) != len(review_request_ids):
178
 
            found_ids = review_requests_qs.values_list(id_field, flat=True)
179
 
            missing_ids = sorted(set(review_request_ids) - set(found_ids))
 
147
        if review_request_ids:
 
148
            if not (isinstance(review_request_ids, list) and
 
149
                    all(isinstance(id, int) for id in review_request_ids)):
 
150
                return JsonResponse(
 
151
                    data={
 
152
                        'stat': 'fail',
 
153
                        'error': 'review_requests must be an array of integers.',
 
154
                    },
 
155
                    status=400)
180
156
 
181
157
            if self.local_site:
182
 
                return JsonResponse(
183
 
                    data={
184
 
                        'stat': 'fail',
185
 
                        'error': ('The following review requests are not '
186
 
                                  'valid for the local site: %r.'
187
 
                                  % missing_ids),
188
 
                    },
189
 
                    status=400)
 
158
                id_field = 'local_id'
 
159
                extra_query = Q(local_id__in=review_request_ids)
190
160
            else:
191
 
                return JsonResponse(
192
 
                    data={
193
 
                        'stat': 'fail',
194
 
                        'error': ('The following review requests are not '
195
 
                                  'valid: %r.'
196
 
                                  % missing_ids),
197
 
                    },
198
 
                    status=400)
 
161
                id_field = 'pk'
 
162
                extra_query = Q(pk__in=review_request_ids)
 
163
 
 
164
            review_requests_qs = (
 
165
                ReviewRequest.objects.public(
 
166
                    user=user,
 
167
                    status=None,
 
168
                    show_all_unpublished=True,
 
169
                    local_site=self.local_site,
 
170
                    extra_query=extra_query)
 
171
            )
 
172
 
 
173
            if op == BatchOperation.PUBLISH:
 
174
                review_requests_qs = review_requests_qs.select_related('draft')
 
175
 
 
176
            # We use in_bulk to deduplicate review requests. It's possible that
 
177
            # the .public() query can return duplicates, and we don't want to
 
178
            # use .distinct() because of performance issues with MySQL. Ideally
 
179
            # we'd just be able to avoid using extra_query and pass in the
 
180
            # id_field to this like we do for reviews below, but local_id isn't
 
181
            # unique (even though in the context of the resulting queryset it
 
182
            # is).
 
183
            review_requests = review_requests_qs.in_bulk()
 
184
 
 
185
            if len(review_requests) != len(review_request_ids):
 
186
                found_ids = [
 
187
                    getattr(rr, id_field)
 
188
                    for rr in review_requests.values()
 
189
                ]
 
190
                missing_ids = sorted(set(review_request_ids) - set(found_ids))
 
191
 
 
192
                if self.local_site:
 
193
                    return JsonResponse(
 
194
                        data={
 
195
                            'stat': 'fail',
 
196
                            'error': ('The following review requests are not '
 
197
                                      'valid for the local site: %r.'
 
198
                                      % missing_ids),
 
199
                        },
 
200
                        status=400)
 
201
                else:
 
202
                    return JsonResponse(
 
203
                        data={
 
204
                            'stat': 'fail',
 
205
                            'error': ('The following review requests are not '
 
206
                                      'valid: %r.'
 
207
                                      % missing_ids),
 
208
                        },
 
209
                        status=400)
 
210
 
 
211
            ordered_review_requests = [
 
212
                item[1]
 
213
                for item in sorted(review_requests.items())
 
214
            ]
 
215
        else:
 
216
            ordered_review_requests = []
199
217
 
200
218
        if op == BatchOperation.PUBLISH:
201
219
            review_ids = batch_data.get('reviews', [])
202
220
            trivial = batch_data.get('trivial', False)
203
221
            archive_after_publish = batch_data.get('archive', False)
204
222
 
205
 
            if not (isinstance(review_ids, list) and
206
 
                    all(isinstance(id, int) for id in review_ids)):
207
 
                return JsonResponse(
208
 
                    data={
209
 
                        'stat': 'fail',
210
 
                        'error': 'reviews must be an array of integers.',
211
 
                    },
212
 
                    status=400)
213
 
 
214
 
            reviews_qs = (
215
 
                Review.objects.accessible(
216
 
                    base_reply_to=Review.objects.ANY,
217
 
                    user=user,
218
 
                    local_site=self.local_site,
219
 
                    extra_query=Q(pk__in=review_ids))
220
 
                .prefetch_related('comments')
221
 
                .select_related('user', 'base_reply_to'))
222
 
 
223
 
            reviews = list(reviews_qs.order_by('pk'))
224
 
 
225
 
            if len(reviews) != len(review_ids):
226
 
                found_ids = reviews_qs.values_list('pk', flat=True)
227
 
                missing_ids = sorted(set(review_ids) - set(found_ids))
228
 
 
229
 
                if self.local_site:
230
 
                    return JsonResponse(
231
 
                        data={
232
 
                            'stat': 'fail',
233
 
                            'error': ('The following reviews are not valid '
234
 
                                      'for the local site: %r.'
235
 
                                      % missing_ids),
236
 
                        },
237
 
                        status=400)
238
 
                else:
239
 
                    return JsonResponse(
240
 
                        data={
241
 
                            'stat': 'fail',
242
 
                            'error': (
243
 
                                'The following reviews are not valid: %r.'
244
 
                                % missing_ids),
245
 
                        },
246
 
                        status=400)
247
 
 
248
 
            reviews = list(reviews_qs)
 
223
            if review_ids:
 
224
                if not (isinstance(review_ids, list) and
 
225
                        all(isinstance(id, int) for id in review_ids)):
 
226
                    return JsonResponse(
 
227
                        data={
 
228
                            'stat': 'fail',
 
229
                            'error': 'reviews must be an array of integers.',
 
230
                        },
 
231
                        status=400)
 
232
 
 
233
                reviews_qs = (
 
234
                    Review.objects.accessible(
 
235
                        base_reply_to=Review.objects.ANY,
 
236
                        user=user,
 
237
                        local_site=self.local_site)
 
238
                    .prefetch_related('comments')
 
239
                    .select_related('user', 'base_reply_to'))
 
240
 
 
241
                # We use in_bulk to deduplicate reviews. It's possible that the
 
242
                # .accessible() query can return duplicates, and we don't want
 
243
                # to use .distinct() because of performance issues with MySQL.
 
244
                reviews = reviews_qs.in_bulk(review_ids)
 
245
 
 
246
                if len(reviews) != len(review_ids):
 
247
                    found_ids = reviews.keys()
 
248
                    missing_ids = sorted(set(review_ids) - set(found_ids))
 
249
 
 
250
                    if self.local_site:
 
251
                        return JsonResponse(
 
252
                            data={
 
253
                                'stat': 'fail',
 
254
                                'error': ('The following reviews are not valid '
 
255
                                          'for the local site: %r.'
 
256
                                          % missing_ids),
 
257
                            },
 
258
                            status=400)
 
259
                    else:
 
260
                        return JsonResponse(
 
261
                            data={
 
262
                                'stat': 'fail',
 
263
                                'error': (
 
264
                                    'The following reviews are not valid: %r.'
 
265
                                    % missing_ids),
 
266
                            },
 
267
                            status=400)
 
268
                review_objects = list(reviews.values())
 
269
            else:
 
270
                review_objects = []
249
271
 
250
272
            return self._publish(
251
273
                request=request,
252
274
                user=user,
253
 
                review_requests=review_requests,
254
 
                reviews=reviews,
 
275
                review_requests=ordered_review_requests,
 
276
                reviews=review_objects,
255
277
                trivial=trivial,
256
278
                archive_after_publish=archive_after_publish)
257
279
        elif op == BatchOperation.CLOSE:
258
280
            return self._close(
259
281
                request=request,
260
282
                user=user,
261
 
                review_requests=review_requests,
 
283
                review_requests=ordered_review_requests,
262
284
                close_type=ReviewRequest.SUBMITTED)
263
285
        elif op == BatchOperation.DISCARD:
264
286
            return self._close(
265
287
                request=request,
266
288
                user=user,
267
 
                review_requests=review_requests,
 
289
                review_requests=ordered_review_requests,
268
290
                close_type=ReviewRequest.DISCARDED)
269
291
        elif op == BatchOperation.ARCHIVE:
270
292
            return self._set_visibility(
271
293
                request=request,
272
294
                user=user,
273
 
                review_requests=review_requests,
 
295
                review_requests=ordered_review_requests,
274
296
                visibility=ReviewRequestVisit.ARCHIVED)
275
297
        elif op == BatchOperation.MUTE:
276
298
            return self._set_visibility(
277
299
                request=request,
278
300
                user=user,
279
 
                review_requests=review_requests,
 
301
                review_requests=ordered_review_requests,
280
302
                visibility=ReviewRequestVisit.MUTED)
281
303
        elif op == BatchOperation.UNARCHIVE:
282
304
            # This also means unmute.
283
305
            return self._set_visibility(
284
306
                request=request,
285
307
                user=user,
286
 
                review_requests=review_requests,
 
308
                review_requests=ordered_review_requests,
287
309
                visibility=ReviewRequestVisit.VISIBLE)
288
310
        else:
289
311
            logger.error('Hit unknown batch operation case: %s',
336
358
                                  % review_request.display_id),
337
359
                    },
338
360
                    status=403)
339
 
            elif not review_request.get_draft(user):
 
361
            elif not review_request.get_draft():
340
362
                return JsonResponse(
341
363
                    data={
342
364
                        'stat': 'fail',