Passed
Push — master ( 7764bf...8fe6c2 )
by Alexander
02:40
created

tcms.testruns.views.CreateTestRunView.post()   B

Complexity

Conditions 7

Size

Total Lines 77
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 56
dl 0
loc 77
rs 7.0399
c 0
b 0
f 0
cc 7
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
# -*- coding: utf-8 -*-
2
3
from datetime import datetime
4
from http import HTTPStatus
5
6
from django.conf import settings
7
from django.contrib import messages
8
from django.contrib.auth.decorators import permission_required
9
from django.contrib.auth import get_user_model
10
from django.contrib.contenttypes.models import ContentType
11
from django.core.exceptions import ObjectDoesNotExist
12
from django.urls import reverse
13
from django.db.models import Count
14
from django.db.models import Q
15
from django.http import HttpResponseRedirect, Http404, JsonResponse
16
from django.shortcuts import get_object_or_404, render
17
from django.utils.decorators import method_decorator
18
from django.utils.translation import ugettext_lazy as _
19
from django.views.decorators.http import require_GET
20
from django.views.decorators.http import require_POST
21
from django.views.generic.base import TemplateView
22
from django.views.generic.base import View
23
24
from django_comments.models import Comment
25
26
from tcms.core.utils import clean_request
27
from tcms.management.models import Priority, Tag
28
from tcms.testcases.models import TestCasePlan, TestCaseStatus, BugSystem
29
from tcms.testcases.views import get_selected_testcases
30
from tcms.testplans.models import TestPlan
31
from tcms.testruns.data import get_run_bug_ids
32
from tcms.testruns.data import TestExecutionDataMixin
33
from tcms.testruns.forms import NewRunForm, SearchRunForm, BaseRunForm
34
from tcms.testruns.models import TestRun, TestExecution, TestExecutionStatus
35
from tcms.issuetracker.types import IssueTrackerType
36
37
38
User = get_user_model()  # pylint: disable=invalid-name
39
40
41
@method_decorator(permission_required('testruns.add_testrun'), name='dispatch')
42
class CreateTestRunView(View):
43
    """Display the create test run page."""
44
45
    template_name = 'testruns/mutable.html'
46
    http_method_names = ['post']
47
48
    def post(self, request):
49
        # If from_plan does not exist will redirect to plans for select a plan
50
        plan_id = request.POST.get('from_plan')
51
        if not plan_id:
52
            messages.add_message(request,
53
                                 messages.ERROR,
54
                                 _('Creating a TestRun requires a TestPlan, select one'))
55
            return HttpResponseRedirect(reverse('plans-search'))
56
57
        if not request.POST.get('case'):
58
            messages.add_message(request,
59
                                 messages.ERROR,
60
                                 _('Creating a TestRun requires at least one TestCase'))
61
            return HttpResponseRedirect(reverse('test_plan_url_short', args=[plan_id]))
62
63
        # Ready to write cases to test plan
64
        test_cases = get_selected_testcases(request)
65
        test_plan = TestPlan.objects.get(plan_id=plan_id)
66
67
        # note: ordered by case_id for test_show_create_new_run_page()
68
        tcs_values = test_cases.select_related('author',
69
                                               'case_status',
70
                                               'category',
71
                                               'priority').order_by('case_id')
72
73
        if request.POST.get('POSTING_TO_CREATE'):
74
            form = NewRunForm(request.POST)
75
            form.populate(product_id=test_plan.product_id)
76
77
            if form.is_valid():
78
                # Process the data in form.cleaned_data
79
                default_tester = form.cleaned_data['default_tester']
80
81
                test_run = TestRun.objects.create(
82
                    product_version=test_plan.product_version,
83
                    stop_date=None,
84
                    summary=form.cleaned_data.get('summary'),
85
                    notes=form.cleaned_data.get('notes'),
86
                    plan=test_plan,
87
                    build=form.cleaned_data['build'],
88
                    manager=form.cleaned_data['manager'],
89
                    default_tester=default_tester,
90
                )
91
92
                assignee_tester = User.objects.filter(username=default_tester).first()
93
94
                loop = 1
95
                for case in form.cleaned_data['case']:
96
                    try:
97
                        tcp = TestCasePlan.objects.get(plan=test_plan, case=case)
98
                        sortkey = tcp.sortkey
99
                    except ObjectDoesNotExist:
100
                        sortkey = loop * 10
101
102
                    test_run.add_case_run(case=case, sortkey=sortkey,
103
                                          assignee=assignee_tester)
104
                    loop += 1
105
106
                return HttpResponseRedirect(
107
                    reverse('testruns-get', args=[test_run.run_id, ])
108
                )
109
110
        else:
111
            form = NewRunForm(initial={
112
                'summary': 'Test run for %s' % test_plan.name,
113
                'manager': test_plan.author.email,
114
                'default_tester': request.user.email,
115
                'notes': '',
116
            })
117
            form.populate(product_id=test_plan.product_id)
118
119
        context_data = {
120
            'test_plan': test_plan,
121
            'test_cases': tcs_values,
122
            'form': form,
123
        }
124
        return render(request, self.template_name, context_data)
125
126
127
@require_GET
128
def search(request):  # pylint: disable=missing-permission-required
129
    form = SearchRunForm(request.GET)
130
    form.populate(product_id=request.GET.get('product'))
131
132
    context_data = {
133
        'form': form,
134
    }
135
    return render(request, 'testruns/search.html', context_data)
136
137
138
def _open_run_get_executions(request, run):
139
    """Prepare for executions list in a TestRun page
140
141
    This is an internal method. Do not call this directly.
142
    """
143
144
    executions = run.case_run.select_related(
145
        'run', 'case'
146
    ).only('run__run_id',
147
           'run__plan',
148
           'status',
149
           'assignee',
150
           'tested_by',
151
           'case_text_version',
152
           'sortkey',
153
           'case__summary',
154
           'case__is_automated',
155
           'case__priority',
156
           'case__category__name'
157
           )
158
159
    # Get the bug count for each execution
160
    # 5. have to show the number of bugs of each execution
161
    executions = executions.annotate(num_bug=Count('case_run_bug', distinct=True))
162
163
    # todo: is this last distinct necessary
164
    executions = executions.distinct()
165
166
    # Continue to search the executionss with conditions
167
    # 4. executions preparing for render executions table
168
    executions = executions.filter(**clean_request(request))
169
    order_by = request.GET.get('order_by')
170
    if order_by:
171
        executions = executions.order_by(order_by)
172
    else:
173
        executions = executions.order_by('sortkey', 'pk')
174
    return executions
175
176
177
def open_run_get_comments_subtotal(case_run_ids):
178
    content_type = ContentType.objects.get_for_model(TestExecution)
179
    query_set = Comment.objects.filter(
180
        content_type=content_type,
181
        site_id=settings.SITE_ID,
182
        object_pk__in=case_run_ids,
183
        is_removed=False).values('object_pk').annotate(comment_count=Count('pk')).order_by(
184
            'object_pk')
185
186
    result = ((int(row['object_pk']), row['comment_count']) for row in query_set)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable row does not seem to be defined.
Loading history...
187
    return dict(result)
188
189
190
def open_run_get_users(case_runs):
191
    tester_ids = set()
192
    assignee_ids = set()
193
    for case_run in case_runs:
194
        if case_run.tested_by_id:
195
            tester_ids.add(case_run.tested_by_id)
196
        if case_run.assignee_id:
197
            assignee_ids.add(case_run.assignee_id)
198
    testers = User.objects.filter(
199
        pk__in=tester_ids).values_list('pk', 'username')
200
    assignees = User.objects.filter(
201
        pk__in=assignee_ids).values_list('pk', 'username')
202
    return (dict(testers.iterator()), dict(assignees.iterator()))
203
204
205
@require_GET
206
def get(request,  # pylint: disable=missing-permission-required
207
        run_id, template_name='run/get.html'):
208
    """Display testrun's detail"""
209
210
    # Get the test run
211
    try:
212
        # 1. get test run itself
213
        test_run = TestRun.objects.select_related().get(run_id=run_id)
214
    except ObjectDoesNotExist:
215
        raise Http404
216
217
    # Get the test executions that belong to the run
218
    # 2. get test run's all executions
219
    test_executions = _open_run_get_executions(request, test_run)
220
221
    status = TestExecutionStatus.objects.only('pk', 'name').order_by('pk')
222
223
    # Count the status
224
    # 3. calculate number of executions of each status
225
    status_stats_result = test_run.stats_executions_status(status)
226
227
    # Get the test execution bugs summary
228
    # 6. get the number of bugs of this run
229
    execution_bugs_count = test_run.get_bug_count()
230
231
    # Get tag list of testcases
232
    # 7. get tags
233
    # Get the list of testcases belong to the run
234
    test_cases = []
235
    for test_execution in test_executions:
236
        test_cases.append(test_execution.case_id)
237
    tags = Tag.objects.filter(case__in=test_cases).values_list('name', flat=True)
238
    tags = list(set(tags))
239
    tags.sort()
240
241
    def walk_executions():
242
        """Walking executions for helping rendering executions table"""
243
244
        priorities = dict(Priority.objects.values_list('pk', 'value'))
245
        testers, assignees = open_run_get_users(test_executions)
246
        execution_pks = []
247
        for execution in test_executions:
248
            execution_pks.append(execution.pk)
249
        comments_subtotal = open_run_get_comments_subtotal(execution_pks)
250
        status = TestExecutionStatus.get_names()
251
252
        for case_run in test_executions:
253
            yield (case_run,
254
                   testers.get(case_run.tested_by_id, None),
255
                   assignees.get(case_run.assignee_id, None),
256
                   priorities.get(case_run.case.priority_id),
257
                   status[case_run.status_id],
258
                   comments_subtotal.get(case_run.pk, 0))
259
260
    context_data = {
261
        'test_run': test_run,
262
        'executions': walk_executions(),
263
        'executions_count': len(test_executions),
264
        'status_stats': status_stats_result,
265
        'execution_bugs_count': execution_bugs_count,
266
        'test_status': status,
267
        'priorities': Priority.objects.filter(is_active=True),
268
        'case_own_tags': tags,
269
        'bug_trackers': BugSystem.objects.all(),
270
    }
271
    return render(request, template_name, context_data)
272
273
274
@permission_required('testruns.change_testrun')
275
def edit(request, run_id):
276
    """Edit test plan view"""
277
278
    try:
279
        test_run = TestRun.objects.select_related().get(run_id=run_id)
280
    except ObjectDoesNotExist:
281
        raise Http404
282
283
    # If the form is submitted
284
    if request.method == "POST":
285
        form = BaseRunForm(request.POST)
286
        form.populate(product_id=test_run.plan.product_id)
287
288
        # FIXME: Error handler
289
        if form.is_valid():
290
            test_run.summary = form.cleaned_data['summary']
291
            # Permission hack
292
            if test_run.manager == request.user or test_run.plan.author == request.user:
293
                test_run.manager = form.cleaned_data['manager']
294
            test_run.default_tester = form.cleaned_data['default_tester']
295
            test_run.build = form.cleaned_data['build']
296
            test_run.product_version = test_run.plan.product_version
297
            test_run.notes = form.cleaned_data['notes']
298
            test_run.save()
299
300
            return HttpResponseRedirect(reverse('testruns-get', args=[run_id, ]))
301
    else:
302
        # Generate a blank form
303
        form = BaseRunForm(initial={
304
            'summary': test_run.summary,
305
            'manager': test_run.manager.email,
306
            'default_tester': (test_run.default_tester and
307
                               test_run.default_tester.email or None),
308
            'version': test_run.product_version_id,
309
            'build': test_run.build_id,
310
            'notes': test_run.notes,
311
        })
312
        form.populate(test_run.plan.product_id)
313
314
    context_data = {
315
        'test_run': test_run,
316
        'test_plan': test_run.plan,
317
        'form': form,
318
    }
319
    return render(request, 'testruns/mutable.html', context_data)
320
321
322
class TestRunReportView(TemplateView, TestExecutionDataMixin):
323
    """Test Run report"""
324
325
    template_name = 'run/report.html'
326
327
    def get_context_data(self, **kwargs):
328
        """Generate report for specific TestRun
329
330
        There are four data source to generate this report.
331
        1. TestRun
332
        2. Test executions included in the TestRun
333
        3. Comments associated with each test execution
334
        4. Statistics
335
        5. bugs
336
        """
337
        run = TestRun.objects.select_related('manager', 'plan').get(pk=kwargs['run_id'])
338
339
        case_runs = TestExecution.objects.filter(
340
            run=run
341
        ).select_related(
342
            'status', 'case', 'tested_by'
343
        ).only(
344
            'close_date',
345
            'status__name',
346
            'case__category__name',
347
            'case__summary', 'case__is_automated',
348
            'tested_by__username'
349
        )
350
        mode_stats = self.stats_mode_executions(case_runs)
351
        summary_stats = self.get_summary_stats(case_runs)
352
353
        execution_bugs = []
354
        bug_system_types = {}
355
        for _bug in get_run_bug_ids(run.pk):
356
            # format the bug URLs based on DB settings
357
            execution_bugs.append((
358
                _bug['bug_id'],
359
                _bug['bug_system__url_reg_exp'] % _bug['bug_id'],
360
            ))
361
            # find out all unique bug tracking systems which were used to record
362
            # bugs in this particular test run. we use this data for reporting
363
            if _bug['bug_system'] not in bug_system_types:
364
                # store a tracker type object for producing the report URL
365
                tracker_class = IssueTrackerType.from_name(_bug['bug_system__tracker_type'])
366
                bug_system = BugSystem.objects.get(pk=_bug['bug_system'])
367
                tracker = tracker_class(bug_system)
368
                bug_system_types[_bug['bug_system']] = (tracker, [])
369
370
            # store the list of bugs as well
371
            bug_system_types[_bug['bug_system']][1].append(_bug['bug_id'])
372
373
        # list of URLs which opens all bugs reported to every different
374
        # issue tracker used in this test run
375
        report_urls = []
376
        for (issue_tracker, ids) in bug_system_types.values():
377
            report_url = issue_tracker.all_issues_link(ids)
378
            # if IT doesn't support this feature or report url is not configured
379
            # the above method will return None
380
            if report_url:
381
                report_urls.append((issue_tracker.tracker.name, report_url))
382
383
        case_run_bugs = self.get_execution_bugs(run.pk)
384
        comments = self.get_execution_comments(run.pk)
385
386
        for case_run in case_runs:
387
            case_run.bugs = case_run_bugs.get(case_run.pk, ())
388
            case_run.user_comments = comments.get(case_run.pk, [])
389
390
        context = super().get_context_data(**kwargs)
391
        context.update({
392
            'test_run': run,
393
            'executions': case_runs,
394
            'executions_count': len(case_runs),
395
            'execution_bugs': execution_bugs,
396
            'mode_stats': mode_stats,
397
            'summary_stats': summary_stats,
398
            'report_urls': report_urls,
399
        })
400
401
        return context
402
403
404
@method_decorator(permission_required('testruns.add_testrun'), name='dispatch')
405
class CloneTestRunView(View):
406
    """Clone cases from filter caserun"""
407
408
    template_name = 'testruns/mutable.html'
409
    http_method_names = ['post']
410
411
    def post(self, request, run_id):
412
        test_run = get_object_or_404(TestRun, run_id=run_id)
413
        confirmed_case_status = TestCaseStatus.get_confirmed()
414
        disabled_cases = 0
415
416
        if request.POST.get('case_run'):
417
            test_cases = []
418
            for test_case_run in test_run.case_run.filter(pk__in=request.POST.getlist('case_run')):
419
                if test_case_run.case.case_status == confirmed_case_status:
420
                    test_cases.append(test_case_run.case)
421
                else:
422
                    disabled_cases += 1
423
        else:
424
            test_cases = None
425
426
        if not test_cases:
427
            messages.add_message(request,
428
                                 messages.ERROR,
429
                                 _('At least one TestCase is required'))
430
            return HttpResponseRedirect(reverse('testruns-get', args=[run_id]))
431
432
        form = NewRunForm(initial={
433
            'summary': _('Clone of ') + test_run.summary,
434
            'notes': test_run.notes,
435
            'manager': test_run.manager,
436
            'build': test_run.build_id,
437
            'default_tester': test_run.default_tester,
438
        })
439
        form.populate(product_id=test_run.plan.product_id)
440
441
        context_data = {
442
            'is_cloning': True,
443
            'disabled_cases': disabled_cases,
444
            'test_plan': test_run.plan,
445
            'test_cases': test_cases,
446
            'form': form,
447
        }
448
        return render(request, self.template_name, context_data)
449
450
451
@permission_required('testruns.change_testrun')
452
def change_status(request, run_id):
453
    """Change test run finished or running"""
454
    test_run = get_object_or_404(TestRun, run_id=run_id)
455
456
    test_run.update_completion_status(request.GET.get('finished') == '1')
457
    test_run.save()
458
459
    return HttpResponseRedirect(reverse('testruns-get', args=[run_id, ]))
460
461
462
@require_POST
463
@permission_required('testruns.delete_testexecution')
464
def remove_execution(request, run_id):
465
    """Remove specific execution from the run"""
466
467
    # Ignore invalid execution ids
468
    execution_ids = []
469
    for item in request.POST.getlist('case_run'):
470
        try:
471
            execution_ids.append(int(item))
472
        except (ValueError, TypeError):
473
            pass
474
475
    # If no execution to remove, no further operation is required, just return
476
    # back to run page immediately.
477
    if not execution_ids:
478
        return HttpResponseRedirect(reverse('testruns-get',
479
                                            args=[run_id, ]))
480
481
    run = get_object_or_404(TestRun.objects.only('pk'), pk=run_id)
482
483
    # Restrict to delete those executions that belongs to run
484
    TestExecution.objects.filter(run_id=run.pk, pk__in=execution_ids).delete()
485
486
    execution_exist = TestExecution.objects.filter(run_id=run.pk).exists()
487
    if execution_exist:
488
        redirect_to = 'testruns-get'
489
    else:
490
        redirect_to = 'add-cases-to-run'
491
492
    return HttpResponseRedirect(reverse(redirect_to, args=[run_id, ]))
493
494
495
@method_decorator(permission_required('testruns.add_testexecution'), name='dispatch')
496
class AddCasesToRunView(View):
497
    """Add cases to a TestRun"""
498
499
    def post(self, request, run_id):
500
        # Selected cases' ids to add to run
501
        test_cases_ids = request.POST.getlist('case')
502
        if not test_cases_ids:
503
            # user clicked Update button without selecting new Test Cases
504
            # to be dded to TestRun
505
            messages.add_message(request,
506
                                 messages.ERROR,
507
                                 _('At least one TestCase is required'))
508
            return HttpResponseRedirect(reverse('add-cases-to-run', args=[run_id]))
509
510
        try:
511
            test_cases_ids = list(map(int, test_cases_ids))
512
        except (ValueError, TypeError):
513
            # this will happen only on malicious requests
514
            messages.add_message(request,
515
                                 messages.ERROR,
516
                                 _('TestCase ID is not a valid integer'))
517
            return HttpResponseRedirect(reverse('add-cases-to-run', args=[run_id]))
518
519
        try:
520
            test_run = TestRun.objects.select_related('plan').only('plan__plan_id').get(
521
                run_id=run_id)
522
        except ObjectDoesNotExist:
523
            raise Http404
524
525
        executions_ids = test_run.case_run.values_list('case', flat=True)
526
527
        # avoid add cases that are already in current run with pk run_id
528
        test_cases_ids = set(test_cases_ids) - set(executions_ids)
529
530
        test_plan = test_run.plan
531
        test_cases = test_run.plan.case.filter(case_status__name='CONFIRMED').select_related(
532
            'default_tester').only('default_tester__id').filter(
533
                case_id__in=test_cases_ids)
534
535
        if request.POST.get('_use_plan_sortkey'):
536
            test_case_pks = (test_case.pk for test_case in test_cases)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable test_case does not seem to be defined.
Loading history...
537
            query_set = TestCasePlan.objects.filter(
538
                plan=test_plan, case__in=test_case_pks).values('case', 'sortkey')
539
            sort_keys_in_plan = dict((row['case'], row['sortkey']) for row in query_set.iterator())
540
            for test_case in test_cases:
541
                sort_key = sort_keys_in_plan.get(test_case.pk, 0)
542
                test_run.add_case_run(case=test_case, sortkey=sort_key)
543
        else:
544
            for test_case in test_cases:
545
                test_run.add_case_run(case=test_case)
546
547
        return HttpResponseRedirect(reverse('testruns-get',
548
                                            args=[test_run.run_id, ]))
549
550
    def get(self, request, run_id):
551
        # information about TestRun, used in the page header
552
        test_run = TestRun.objects.select_related(
553
            'plan', 'manager', 'build'
554
        ).only(
555
            'plan', 'plan__name',
556
            'manager__email', 'build__name'
557
        ).get(run_id=run_id)
558
559
        # select all CONFIRMED cases from the TestPlan that is a parent
560
        # of this particular TestRun
561
        rows = TestCasePlan.objects.values(
562
            'case',
563
            'case__create_date', 'case__summary',
564
            'case__category__name',
565
            'case__priority__value',
566
            'case__author__username'
567
        ).filter(
568
            plan_id=test_run.plan,
569
            case__case_status=TestCaseStatus.objects.filter(name='CONFIRMED').first().pk
570
        ).order_by('case')  # order b/c of PostgreSQL
571
572
        # also grab a list of all TestCase IDs which are already present in the
573
        # current TestRun so we can mark them as disabled and not allow them to
574
        # be selected
575
        executions = TestExecution.objects.filter(run=run_id).values_list('case', flat=True)
576
577
        data = {
578
            'test_run': test_run,
579
            'confirmed_cases': rows,
580
            'confirmed_cases_count': rows.count(),
581
            'executions_count': len(executions),
582
            'exist_case_run_ids': executions,
583
        }
584
585
        return render(request, 'run/assign_case.html', data)
586
587
588
@require_GET
589
def cc(request, run_id):  # pylint: disable=invalid-name
590
    """Add or remove cc from a test run"""
591
592
    test_run = get_object_or_404(TestRun, run_id=run_id)
593
    action = request.GET.get('do')
594
    username_or_email = request.GET.get('user')
595
    context_data = {'test_run': test_run, 'is_ajax': True}
596
597
    if action:
598
        if not username_or_email:
599
            context_data['message'] = 'User name or email is required by this operation'
600
        else:
601
            try:
602
                user = User.objects.get(
603
                    Q(username=username_or_email) |
604
                    Q(email=username_or_email)
605
                )
606
            except ObjectDoesNotExist:
607
                context_data['message'] = 'The user you typed does not exist in database'
608
            else:
609
                if action == 'add':
610
                    test_run.add_cc(user=user)
611
612
                if action == 'remove':
613
                    test_run.remove_cc(user=user)
614
615
    return render(request, 'run/get_cc.html', context_data)
616
617
618
@require_POST
619
@permission_required('testruns.change_testexecution')
620
def update_case_run_text(request, run_id):
621
    """Update the IDLE cases to newest text"""
622
623
    test_run = get_object_or_404(TestRun, run_id=run_id)
624
625
    if request.POST.get('case_run'):
626
        executions = test_run.case_run.filter(pk__in=request.POST.getlist('case_run'))
627
    else:
628
        executions = test_run.case_run.all()
629
630
    executions = executions.filter(status__name='IDLE')
631
632
    count = 0
633
    updated_executions = ''
634
    for execution in executions:
635
        latest_version = execution.case.history.latest().history_id
636
        if execution.case_text_version != latest_version:
637
            count += 1
638
            updated_executions += '<li>%s: %s -> %s</li>' % (
639
                execution.case.summary, execution.case_text_version, latest_version
640
            )
641
            execution.case_text_version = latest_version
642
            execution.save()
643
644
    info = "<p>%s</p><ul>%s</ul>" % (_("%d CaseRun(s) updated:") % count, updated_executions)
645
    message_level = messages.INFO
646
    if count:
647
        message_level = messages.SUCCESS
648
649
    messages.add_message(request, message_level, info)
650
    return HttpResponseRedirect(reverse('testruns-get', args=[run_id]))
651
652
653
def get_caseruns_of_runs(runs, kwargs=None):
654
    """
655
    Filtering argument -
656
        priority
657
        tester
658
        plan tag
659
    """
660
661
    if kwargs is None:
662
        kwargs = {}
663
    plan_tag = kwargs.get('plan_tag', None)
664
    if plan_tag:
665
        runs = runs.filter(plan__tag__name=plan_tag)
666
    caseruns = TestExecution.objects.filter(run__in=runs)
667
    priority = kwargs.get('priority', None)
668
    if priority:
669
        caseruns = caseruns.filter(case__priority__pk=priority)
670
    tester = kwargs.get('tester', None)
671
    if not tester:
672
        caseruns = caseruns.filter(tested_by=None)
673
    if tester:
674
        caseruns = caseruns.filter(tested_by__pk=tester)
675
    status = kwargs.get('status', None)
676
    if status:
677
        caseruns = caseruns.filter(status__name__iexact=status)
678
    return caseruns
679
680
681
@method_decorator(permission_required('testruns.change_testexecution'), name='dispatch')
682
class UpdateAssigneeView(View):
683
    """Updates TestExecution.assignee. Called from the front-end."""
684
685
    http_method_names = ['post']
686
687
    def post(self, request):
688
        assignee = request.POST.get('assignee')
689
        try:
690
            user = User.objects.get(username=assignee)
691
        except User.DoesNotExist:
692
            try:
693
                user = User.objects.get(email=assignee)
694
            except User.DoesNotExist:
695
                return JsonResponse({'rc': 1,
696
                                     'response': _('User %s not found!' % assignee)},
697
                                    status=HTTPStatus.NOT_FOUND)
698
699
        object_ids = request.POST.getlist('ids[]')
700
701
        for caserun_pk in object_ids:
702
            execution = get_object_or_404(TestExecution, pk=int(caserun_pk))
703
            execution.assignee = user
704
            execution.save()
705
706
        return JsonResponse({'rc': 0, 'response': 'ok'})
707
708
709
@method_decorator(permission_required('testruns.change_testexecution'), name='dispatch')
710
class UpdateCaseRunStatusView(View):
711
    """Updates TestExecution.status_id. Called from the front-end."""
712
713
    http_method_names = ['post']
714
715
    def post(self, request):
716
        status_id = int(request.POST.get('status_id'))
717
        object_ids = request.POST.getlist('object_pk[]')
718
719
        for caserun_pk in object_ids:
720
            execution = get_object_or_404(TestExecution, pk=int(caserun_pk))
721
            execution.status_id = status_id
722
            execution.tested_by = request.user
723
            execution.close_date = datetime.now()
724
            execution.save()
725
726
        return JsonResponse({'rc': 0, 'response': 'ok'})
727