1
|
|
|
# pylint: disable=no-self-use |
2
|
|
|
|
3
|
|
|
from django import forms |
4
|
|
|
from django.conf import settings |
5
|
|
|
from django.contrib import admin, messages |
6
|
|
|
from django.forms.widgets import Select |
7
|
|
|
from django.http import HttpResponseRedirect |
8
|
|
|
from django.urls import reverse |
9
|
|
|
from django.utils.module_loading import import_string |
10
|
|
|
from django.utils.translation import gettext_lazy as _ |
11
|
|
|
|
12
|
|
|
from tcms.core.admin import ObjectPermissionsAdminMixin |
13
|
|
|
from tcms.core.history import ReadOnlyHistoryAdmin |
14
|
|
|
from tcms.testcases.models import BugSystem, Category, TestCase, TestCaseStatus |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
class TestCaseStatusAdmin(admin.ModelAdmin): |
18
|
|
|
list_display = ("id", "name", "is_confirmed") |
19
|
|
|
ordering = ["-is_confirmed", "id"] |
20
|
|
|
fieldsets = [ |
21
|
|
|
( |
22
|
|
|
"", |
23
|
|
|
{ |
24
|
|
|
"fields": ("name", "description", "is_confirmed"), |
25
|
|
|
"description": "<h1>%s</h1>" |
26
|
|
|
% _( |
27
|
|
|
"""For more information about customizing test case statuses see |
28
|
|
|
<a href="https://kiwitcms.readthedocs.io/en/latest/admin.html#test-case-statuses"> |
29
|
|
|
the documentation</a>!""" |
30
|
|
|
), |
31
|
|
|
}, |
32
|
|
|
), |
33
|
|
|
] |
34
|
|
|
|
35
|
|
|
@admin.options.csrf_protect_m |
36
|
|
|
def delete_view(self, request, object_id, extra_context=None): |
37
|
|
|
obj = self.model.objects.get(pk=object_id) |
38
|
|
|
|
39
|
|
|
if ( |
40
|
|
|
not self.model.objects.filter(is_confirmed=obj.is_confirmed) |
41
|
|
|
.exclude(pk=object_id) |
42
|
|
|
.exists() |
43
|
|
|
): |
44
|
|
|
messages.add_message( |
45
|
|
|
request, |
46
|
|
|
messages.ERROR, |
47
|
|
|
_("1 confirmed & 1 uncomfirmed status required!"), |
48
|
|
|
) |
49
|
|
|
|
50
|
|
|
return HttpResponseRedirect( |
51
|
|
|
reverse("admin:testcases_testcasestatus_changelist") |
52
|
|
|
) |
53
|
|
|
|
54
|
|
|
return super().delete_view(request, object_id, extra_context) |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
class TestCaseAdmin(ObjectPermissionsAdminMixin, ReadOnlyHistoryAdmin): |
58
|
|
|
def add_view(self, request, form_url="", extra_context=None): |
59
|
|
|
return HttpResponseRedirect(reverse("testcases-new")) |
60
|
|
|
|
61
|
|
|
def change_view(self, request, object_id, form_url="", extra_context=None): |
62
|
|
|
return HttpResponseRedirect(reverse("testcases-get", args=[object_id])) |
63
|
|
|
|
64
|
|
|
|
65
|
|
|
class CategoryAdmin(admin.ModelAdmin): |
66
|
|
|
search_fields = ("name",) |
67
|
|
|
list_display = ("id", "name", "product", "description") |
68
|
|
|
list_filter = ("product",) |
69
|
|
|
|
70
|
|
|
|
71
|
|
|
class IssueTrackerTypeSelectWidget(Select): |
72
|
|
|
""" |
73
|
|
|
A select widget which displays the names of all classes |
74
|
|
|
derived from IssueTrackerType. Skip IssueTrackerType |
75
|
|
|
because it is doesn't provide implementations for most of its methods. |
76
|
|
|
""" |
77
|
|
|
|
78
|
|
|
_choices = None |
79
|
|
|
|
80
|
|
|
@property |
81
|
|
|
def choices(self): |
82
|
|
|
if self._choices is None: |
83
|
|
|
self._choices = self._types_as_choices() |
84
|
|
|
return self._choices |
85
|
|
|
|
86
|
|
|
@choices.setter |
87
|
|
|
def choices(self, _): |
88
|
|
|
# ChoiceField.__init__ sets ``self.choices = choices`` |
89
|
|
|
# which would override ours. |
90
|
|
|
pass |
91
|
|
|
|
92
|
|
|
@staticmethod |
93
|
|
|
def _types_as_choices(): |
94
|
|
|
return (("", ""),) + tuple( |
95
|
|
|
zip(settings.EXTERNAL_BUG_TRACKERS, settings.EXTERNAL_BUG_TRACKERS) |
96
|
|
|
) |
97
|
|
|
|
98
|
|
|
|
99
|
|
|
class IssueTrackerTypeField(forms.ChoiceField): |
100
|
|
|
"""Special choice field which uses the widget above""" |
101
|
|
|
|
102
|
|
|
widget = IssueTrackerTypeSelectWidget |
103
|
|
|
|
104
|
|
|
def valid_value(self, value): |
105
|
|
|
return True |
106
|
|
|
|
107
|
|
|
|
108
|
|
|
class BugSystemAdminForm(forms.ModelForm): |
109
|
|
|
# make password show asterisks |
110
|
|
|
api_password = forms.CharField( |
111
|
|
|
widget=forms.PasswordInput(render_value=True), required=False |
112
|
|
|
) |
113
|
|
|
|
114
|
|
|
# select only tracker types for which we have available integrations |
115
|
|
|
tracker_type = IssueTrackerTypeField( # pylint:disable=form-field-help-text-used |
116
|
|
|
help_text="This determines how Kiwi TCMS integrates with the IT system", |
117
|
|
|
) |
118
|
|
|
|
119
|
|
|
hc_bug_url = forms.CharField( # pylint: disable=form-field-label-used, form-field-help-text-used |
120
|
|
|
required=False, |
121
|
|
|
max_length=1024, |
122
|
|
|
label=_("Bug URL"), |
123
|
|
|
help_text=_( |
124
|
|
|
"Kiwi TCMS will try fetching details for the given bug URL using the " |
125
|
|
|
"integration defined above! Click the `Save and continue` button and " |
126
|
|
|
"watch out for messages at the top of the screen. <strong>WARNING:</strong> " |
127
|
|
|
"in case of failures some issue trackers will fall back to fetching details " |
128
|
|
|
"via the OpenGraph protocol. In that case the result will include field named " |
129
|
|
|
"`from_open_graph`." |
130
|
|
|
), |
131
|
|
|
widget=admin.widgets.AdminTextInputWidget, |
132
|
|
|
) |
133
|
|
|
|
134
|
|
|
class Meta: |
135
|
|
|
model = BugSystem |
136
|
|
|
fields = "__all__" |
137
|
|
|
|
138
|
|
|
|
139
|
|
|
class BugSystemAdmin(admin.ModelAdmin): |
140
|
|
|
search_fields = ("name",) |
141
|
|
|
list_display = ("id", "name", "base_url") |
142
|
|
|
fieldsets = [ |
143
|
|
|
( |
144
|
|
|
"", |
145
|
|
|
{ |
146
|
|
|
"fields": ("name",), |
147
|
|
|
}, |
148
|
|
|
), |
149
|
|
|
( |
150
|
|
|
_("External Issue Tracker Integration"), |
151
|
|
|
{ |
152
|
|
|
"fields": ( |
153
|
|
|
"tracker_type", |
154
|
|
|
"base_url", |
155
|
|
|
"api_url", |
156
|
|
|
"api_username", |
157
|
|
|
"api_password", |
158
|
|
|
), |
159
|
|
|
"description": _( |
160
|
|
|
"""<h1>Warning: read the |
161
|
|
|
<a href="http://kiwitcms.readthedocs.io/en/latest/admin.html#configure-external-bug-trackers"> |
162
|
|
|
Configure external bug trackers</a> section before editting the values below!</h1>""" |
163
|
|
|
), |
164
|
|
|
}, |
165
|
|
|
), |
166
|
|
|
( |
167
|
|
|
_("Configuration health check"), |
168
|
|
|
{ |
169
|
|
|
"fields": ("hc_bug_url",), |
170
|
|
|
}, |
171
|
|
|
), |
172
|
|
|
] |
173
|
|
|
form = BugSystemAdminForm |
174
|
|
|
|
175
|
|
|
def save_model(self, request, obj, form, change): |
176
|
|
|
super().save_model(request, obj, form, change) |
177
|
|
|
# try health check |
178
|
|
|
bug_url = form.cleaned_data["hc_bug_url"] |
179
|
|
|
if bug_url: |
180
|
|
|
try: |
181
|
|
|
tracker = import_string(obj.tracker_type)(obj, request) |
182
|
|
|
if not tracker: |
183
|
|
|
raise RuntimeError(_("Failed creating Issue Tracker")) |
184
|
|
|
|
185
|
|
|
details = tracker.details(bug_url) |
186
|
|
|
messages.add_message( |
187
|
|
|
request, |
188
|
|
|
messages.SUCCESS, |
189
|
|
|
_("Issue Tracker configuration check passed"), |
190
|
|
|
) |
191
|
|
|
messages.add_message( |
192
|
|
|
request, |
193
|
|
|
messages.SUCCESS, |
194
|
|
|
details, |
195
|
|
|
) |
196
|
|
|
except Exception as err: # pylint: disable=broad-except |
197
|
|
|
messages.add_message( |
198
|
|
|
request, |
199
|
|
|
messages.ERROR, |
200
|
|
|
_("Issue Tracker configuration check failed"), |
201
|
|
|
) |
202
|
|
|
messages.add_message( |
203
|
|
|
request, |
204
|
|
|
messages.ERROR, |
205
|
|
|
err, |
206
|
|
|
) |
207
|
|
|
|
208
|
|
|
|
209
|
|
|
admin.site.register(BugSystem, BugSystemAdmin) |
210
|
|
|
admin.site.register(Category, CategoryAdmin) |
211
|
|
|
admin.site.register(TestCase, TestCaseAdmin) |
212
|
|
|
admin.site.register(TestCaseStatus, TestCaseStatusAdmin) |
213
|
|
|
|