tcms.testcases.views.CloneTestCaseView.post()   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 38
rs 8.3946
c 0
b 0
f 0
cc 6
nop 2
1
# -*- coding: utf-8 -*-
2
3
from django.contrib import messages
4
from django.contrib.auth.decorators import permission_required
5
from django.http import HttpResponseRedirect
6
from django.shortcuts import get_object_or_404, render
7
from django.urls import reverse
8
from django.utils.decorators import method_decorator
9
from django.utils.translation import gettext_lazy as _
10
from django.views.generic import DetailView
11
from django.views.generic.base import TemplateView, View
12
from django.views.generic.edit import CreateView, UpdateView
13
from guardian.decorators import permission_required as object_permission_required
14
15
from tcms.testcases.forms import (
16
    CaseNotifyFormSet,
17
    CloneCaseForm,
18
    SearchCaseForm,
19
    TestCaseForm,
20
)
21
from tcms.testcases.models import Template, TestCase
22
from tcms.testplans.models import TestPlan
23
24
25
def plan_from_request_or_none(request):  # pylint: disable=missing-permission-required
26
    """Get TestPlan from REQUEST
27
28
    This method relies on the existence of from_plan within REQUEST.
29
    """
30
    test_plan_id = request.POST.get("from_plan") or request.GET.get("from_plan")
31
    if not test_plan_id:
32
        return None
33
    return get_object_or_404(TestPlan, pk=test_plan_id)
34
35
36
@method_decorator(permission_required("testcases.add_testcase"), name="dispatch")
37
class NewCaseView(CreateView):
38
    model = TestCase
39
    form_class = TestCaseForm
40
    template_name = "testcases/mutable.html"
41
42
    def get_form(self, form_class=None):
43
        form = super().get_form(form_class)
44
        # clear fields which are set dynamically via JavaScript
45
        form.populate(self.request.POST.get("product", -1))
46
        return form
47
48
    def get_form_kwargs(self):
49
        kwargs = super().get_form_kwargs()
50
        kwargs["initial"].update(  # pylint: disable=objects-update-used
51
            {
52
                "author": self.request.user,
53
            }
54
        )
55
56
        test_plan = plan_from_request_or_none(self.request)
57
        if test_plan:
58
            kwargs["initial"]["product"] = test_plan.product_id
59
60
        return kwargs
61
62
    def get_context_data(self, **kwargs):
63
        context = super().get_context_data(**kwargs)
64
        context["test_plan"] = plan_from_request_or_none(self.request)
65
        context["notify_formset"] = kwargs.get("notify_formset") or CaseNotifyFormSet()
66
        context["templates"] = Template.objects.all()
67
        return context
68
69
    def form_valid(self, form):
70
        test_plan = plan_from_request_or_none(self.request)
71
72
        notify_formset = CaseNotifyFormSet(self.request.POST)
73
        if notify_formset.is_valid():
74
            test_case = form.save()
75
            if test_plan:
76
                test_plan.add_case(test_case)
77
78
            notify_formset.instance = test_case
79
            notify_formset.save()
80
81
            return HttpResponseRedirect(reverse("testcases-get", args=[test_case.pk]))
82
83
        # taken from FormMixin.form_invalid()
84
        return self.render_to_response(
85
            self.get_context_data(notify_formset=notify_formset)
86
        )
87
88
89
@method_decorator(permission_required("testcases.view_testcase"), name="dispatch")
90
class TestCaseSearchView(TemplateView):
91
    """
92
    Shows the search form which uses JSON RPC to fetch the results
93
    """
94
95
    template_name = "testcases/search.html"
96
97
    def get_context_data(self, **kwargs):
98
        form = SearchCaseForm(self.request.GET)
99
        if self.request.GET.get("product"):
100
            form.populate(product_id=self.request.GET["product"])
101
        else:
102
            form.populate()
103
104
        return {
105
            "form": form,
106
        }
107
108
109
@method_decorator(
110
    object_permission_required(
111
        "testcases.view_testcase", (TestCase, "pk", "pk"), accept_global_perms=True
112
    ),
113
    name="dispatch",
114
)
115
class TestCaseGetView(DetailView):
116
117
    model = TestCase
118
    template_name = "testcases/get.html"
119
    http_method_names = ["get"]
120
121
    def get_context_data(self, **kwargs):
122
        context = super().get_context_data(**kwargs)
123
        context["executions"] = self.object.executions.select_related(
124
            "run", "tested_by", "assignee", "case", "status"
125
        ).order_by("run__plan", "run")
126
        context["OBJECT_MENU_ITEMS"] = [
127
            (
128
                "...",
129
                [
130
                    (
131
                        _("Edit"),
132
                        reverse("testcases-edit", args=[self.object.pk]),
133
                    ),
134
                    (
135
                        _("Clone"),
136
                        reverse("testcases-clone") + f"?c={self.object.pk}",
137
                    ),
138
                    (
139
                        _("History"),
140
                        f"/admin/testcases/testcase/{self.object.pk}/history/",
141
                    ),
142
                    ("-", "-"),
143
                    (
144
                        _("Object permissions"),
145
                        reverse(
146
                            "admin:testcases_testcase_permissions",
147
                            args=[self.object.pk],
148
                        ),
149
                    ),
150
                    ("-", "-"),
151
                    (
152
                        _("Delete"),
153
                        reverse(
154
                            "admin:testcases_testcase_delete",
155
                            args=[self.object.pk],
156
                        ),
157
                    ),
158
                ],
159
            )
160
        ]
161
162
        return context
163
164
165
@method_decorator(
166
    object_permission_required(
167
        "testcases.change_testcase", (TestCase, "pk", "pk"), accept_global_perms=True
168
    ),
169
    name="dispatch",
170
)
171
class EditTestCaseView(UpdateView):
172
173
    model = TestCase
174
    template_name = "testcases/mutable.html"
175
    form_class = TestCaseForm
176
177
    def form_valid(self, form):
178
        notify_formset = CaseNotifyFormSet(self.request.POST, instance=self.object)
179
        if notify_formset.is_valid():
180
            notify_formset.save()
181
            return super().form_valid(form)
182
183
        # taken from FormMixin.form_invalid()
184
        return self.render_to_response(
185
            self.get_context_data(notify_formset=notify_formset)
186
        )
187
188
    def get_context_data(self, **kwargs):
189
        context = super().get_context_data(**kwargs)
190
        context["notify_formset"] = kwargs.get("notify_formset") or CaseNotifyFormSet(
191
            instance=self.object
192
        )
193
        return context
194
195
    def get_form(self, form_class=None):
196
        form = super().get_form(form_class)
197
        if self.request.POST.get("product"):
198
            form.populate(product_id=self.request.POST["product"])
199
        else:
200
            form.populate(product_id=self.object.category.product_id)
201
        return form
202
203
    def get_initial(self):
204
        default_tester = None
205
        if self.object.default_tester_id:
206
            default_tester = self.object.default_tester.email
207
208
        return {
209
            "product": self.object.category.product_id,
210
            "default_tester": default_tester,
211
        }
212
213
214
@method_decorator(permission_required("testcases.add_testcase"), name="dispatch")
215
class CloneTestCaseView(View):
216
    """Clone one case or multiple case into other plan or plans"""
217
218
    template_name = "testcases/clone.html"
219
    http_method_names = ["get", "post"]
220
221
    def post(self, request):
222
        if not self._is_request_data_valid(request):
223
            return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
224
225
        # Do the clone action
226
        clone_form = CloneCaseForm(request.POST)
227
        clone_form.populate(case_ids=request.POST.getlist("case"))
228
229
        if clone_form.is_valid():
230
            for tc_src in clone_form.cleaned_data["case"]:
231
                tc_dest = tc_src.clone(request.user, clone_form.cleaned_data["plan"])
232
233
            # Detect the number of items and redirect to correct one
234
            if len(clone_form.cleaned_data["case"]) == 1:
235
                return HttpResponseRedirect(
236
                    reverse(
237
                        "testcases-get",
238
                        args=[
239
                            tc_dest.pk,
0 ignored issues
show
introduced by
The variable tc_dest does not seem to be defined in case the for loop on line 230 is not entered. Are you sure this can never be the case?
Loading history...
240
                        ],
241
                    )
242
                )
243
244
            if len(clone_form.cleaned_data["plan"]) == 1:
245
                test_plan = clone_form.cleaned_data["plan"][0]
246
                return HttpResponseRedirect(
247
                    reverse("test_plan_url_short", args=[test_plan.pk])
248
                )
249
250
            # Otherwise tell the user the clone action is successful
251
            messages.add_message(
252
                request, messages.SUCCESS, _("TestCase cloning was successful")
253
            )
254
            return HttpResponseRedirect(reverse("plans-search"))
255
256
        # invalid form
257
        messages.add_message(request, messages.ERROR, clone_form.errors)
258
        return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
259
260
    def get(self, request):
261
        if not self._is_request_data_valid(request, "c"):
262
            return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
263
264
        # account for short param names in URI
265
        get_params = request.GET.copy()
266
        get_params.setlist("case", request.GET.getlist("c"))
267
        del get_params["c"]
268
269
        clone_form = CloneCaseForm(get_params)
270
        clone_form.populate(case_ids=get_params.getlist("case"))
271
272
        context = {
273
            "form": clone_form,
274
        }
275
        return render(request, self.template_name, context)
276
277
    @staticmethod
278
    def _is_request_data_valid(request, field_name="case"):
279
        request_data = getattr(request, request.method)
280
281
        if field_name not in request_data:
282
            messages.add_message(
283
                request, messages.ERROR, _("At least one TestCase is required")
284
            )
285
            return False
286
287
        return True
288