Passed
Push — master ( 62d7d9...8230b1 )
by Alexander
03:29
created

tcms.core.ajax.update_bugs_to_caseruns()   C

Complexity

Conditions 10

Size

Total Lines 37
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 37
rs 5.9999
c 0
b 0
f 0
cc 10
nop 1

How to fix   Complexity   

Complexity

Complex classes like tcms.core.ajax.update_bugs_to_caseruns() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
"""
3
Shared functions for plan/case/run.
4
5
Most of these functions are use for Ajax.
6
"""
7
from http import HTTPStatus
8
9
from django.db.models import Count
10
from django.http import JsonResponse
11
from django.views.generic.base import View
12
from django.contrib.auth import get_user_model
13
from django.utils.decorators import method_decorator
14
from django.views.decorators.http import require_POST
15
from django.shortcuts import render
16
from django.utils.translation import ugettext_lazy as _
17
from django.contrib.auth.decorators import permission_required
18
19
from tcms.testcases.models import TestCase
20
from tcms.testcases.models import TestCaseTag
21
from tcms.testplans.models import TestPlan, TestPlanTag
22
from tcms.testruns.models import TestExecution, TestRunTag
23
from tcms.core.helpers.comments import add_comment
24
25
26
def tags(request):
27
    """
28
        Get tags for TestPlan or TestCase.
29
        Also counts how many times the same tag has been used for
30
        different objects. Used in TP -> Tags and TC -> Tags tabs!
31
    """
32
33
    template_name, obj = _TagObjects(request).get()
34
35
    all_tags = obj.tag.all().order_by('pk')
36
    test_plan_tags = TestPlanTag.objects.filter(
37
        tag__in=all_tags).values('tag').annotate(num_plans=Count('tag')).order_by('tag')
38
    test_case_tags = TestCaseTag.objects.filter(
39
        tag__in=all_tags).values('tag').annotate(num_cases=Count('tag')).order_by('tag')
40
    test_run_tags = TestRunTag.objects.filter(
41
        tag__in=all_tags).values('tag').annotate(num_runs=Count('tag')).order_by('tag')
42
43
    plan_counter = _TagCounter('num_plans', test_plan_tags)
44
    case_counter = _TagCounter('num_cases', test_case_tags)
45
    run_counter = _TagCounter('num_runs', test_run_tags)
46
47
    for tag in all_tags:
48
        tag.num_plans = plan_counter.calculate_tag_count(tag)
49
        tag.num_cases = case_counter.calculate_tag_count(tag)
50
        tag.num_runs = run_counter.calculate_tag_count(tag)
51
52
    api_module = 'NotExisting'
53
    if isinstance(obj, TestPlan):
54
        api_module = 'TestPlan'
55
56
    if isinstance(obj, TestCase):
57
        api_module = 'TestCase'
58
59
    context_data = {
60
        'tags': all_tags,
61
        'object': obj,
62
        'api_module': api_module,
63
    }
64
    # todo: convert this method into returning pure JSON and
65
    # render inside the browser. Also move it under management.views
66
    return render(request, template_name, context_data)
67
68
69
class _TagObjects:
70
    """ Used for getting the chosen object(TestPlan, TestCase or TestRun) from the database """
71
72
    def __init__(self, request):
73
        """
74
        :param request: An HTTP GET request, containing the primary key
75
                        and the type of object to be selected
76
        :type request: HttpRequest
77
        """
78
        for obj in ['plan', 'case']:
79
            if request.GET.get(obj):
80
                self.object = obj
81
                self.object_pk = request.GET.get(obj)
82
                break
83
84
    def get(self):
85
        func = getattr(self, self.object)
86
        return func()
87
88
    def plan(self):
89
        return 'management/get_tag.html', TestPlan.objects.get(pk=self.object_pk)
90
91
    def case(self):
92
        return 'management/get_tag.html', TestCase.objects.get(pk=self.object_pk)
93
94
95
class _TagCounter:  # pylint: disable=too-few-public-methods
96
    """ Used for counting the number of times a tag is assigned to TestRun/TestCase/TestPlan """
97
98
    def __init__(self, key, test_tags):
99
        """
100
         :param key: either 'num_plans', 'num_cases', 'num_runs', depending on what you want count
101
         :type key: str
102
         :param test_tags: query set, containing the Tag->Object relationship, ordered by tag and
103
                            annotated by key
104
            e.g. TestPlanTag, TestCaseTag ot TestRunTag
105
         :type test_tags: QuerySet
106
        """
107
        self.key = key
108
        self.test_tags = iter(test_tags)
109
        self.counter = {'tag': 0}
110
111
    def calculate_tag_count(self, tag):
112
        """
113
        :param tag: the tag you do the counting for
114
        :type tag: :class:`tcms.management.models.Tag`
115
        :return: the number of times a tag is assigned to object
116
        :rtype: int
117
        """
118
        if self.counter['tag'] != tag.pk:
119
            try:
120
                self.counter = self.test_tags.__next__()
121
            except StopIteration:
122
                return 0
123
124
        if tag.pk == self.counter['tag']:
125
            return self.counter[self.key]
126
        return 0
127
128
129
def say_no(error_msg):
130
    return JsonResponse({'rc': 1, 'response': error_msg})
131
132
133
def say_yes():
134
    return JsonResponse({'rc': 0, 'response': 'ok'})
135
136
137
@method_decorator(permission_required('testcases.change_testcase'), name='dispatch')
138
class UpdateTestCaseActorsView(View):
139
    """
140
        Updates TestCase.default_tester_id or TestCase.reviewer_id.
141
        Called from the front-end.
142
    """
143
144
    http_method_names = ['post']
145
146
    def post(self, request):
147
        username = request.POST.get('username')
148
        User = get_user_model()  # pylint: disable=invalid-name
149
        try:
150
            user = User.objects.get(username=username)
151
        except User.DoesNotExist:
152
            try:
153
                user = User.objects.get(email=username)
154
            except User.DoesNotExist:
155
                return JsonResponse({'rc': 1,
156
                                     'response': _('User %s not found!' % username)},
157
                                    status=HTTPStatus.NOT_FOUND)
158
159
        what_to_update = request.POST.get('what_to_update')
160
        case_ids = request.POST.getlist('case[]')
161
        for test_case in TestCase.objects.filter(pk__in=case_ids):
162
            if what_to_update == 'default_tester':
163
                test_case.default_tester_id = user.pk
164
            elif what_to_update == 'reviewer':
165
                test_case.reviewer_id = user.pk
166
167
            test_case.save()
168
169
        return JsonResponse({'rc': 0, 'response': 'ok'})
170
171
172
@require_POST
173
def comment_case_runs(request):
174
    """
175
    Add comment to one or more caseruns at a time.
176
    """
177
    data = request.POST.copy()
178
    comment = data.get('comment', None)
179
    if not comment:
180
        return say_no('Comments needed')
181
    run_ids = []
182
    for run_id in data.get('run', '').split(','):
183
        if run_id:
184
            run_ids.append(run_id)
185
    if not run_ids:
186
        return say_no('No runs selected.')
187
    runs = TestExecution.objects.filter(pk__in=run_ids).only('pk')
188
    if not runs:
189
        return say_no('No caserun found.')
190
    add_comment(runs, comment, request.user)
191
    return say_yes()
192