Passed
Push — master ( 2b96c9...750a3a )
by Alexander
02:50
created

tcms.core.views.DashboardView.get_context_data()   B

Complexity

Conditions 3

Size

Total Lines 70
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 70
rs 8.8
c 0
b 0
f 0
cc 3
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
import os
3
import subprocess  # nosec:B404:import_subprocess
4
import time
5
6
from django import http
7
from django.conf import settings
8
from django.contrib import messages
9
from django.contrib.auth.decorators import login_required
10
from django.contrib.sites.models import Site
11
from django.db import DEFAULT_DB_ALIAS, connections
12
from django.db.migrations.executor import MigrationExecutor
13
from django.db.models import Count, Q
14
from django.http import HttpResponseRedirect, StreamingHttpResponse
15
from django.template import loader
16
from django.urls import reverse
17
from django.utils import timezone, translation
18
from django.utils.decorators import method_decorator
19
from django.utils.safestring import mark_safe
20
from django.utils.translation import gettext_lazy as _
21
from django.utils.translation import trans_real
22
from django.views import i18n
23
from django.views.decorators.csrf import requires_csrf_token
24
from django.views.generic.base import TemplateView, View
25
26
from tcms.testplans.models import TestPlan
27
from tcms.testruns.models import TestRun
28
29
30
@method_decorator(
31
    login_required, name="dispatch"
32
)  # pylint: disable=missing-permission-required
33
class DashboardView(TemplateView):
34
35
    template_name = "dashboard.html"
36
37
    def get_context_data(self, **kwargs):
38
        # Check if domain is configured
39
        site = Site.objects.get(pk=settings.SITE_ID)
40
        doc_url = "https://kiwitcms.readthedocs.io/en/latest/admin.html#configure-kiwi-s-base-url"
41
        if site.domain == "127.0.0.1:8000":
42
            messages.add_message(
43
                self.request,
44
                messages.ERROR,
45
                mark_safe(  # nosec:B308:B703
46
                    _(
47
                        "Base URL is not configured! "
48
                        'See <a href="%(doc_url)s">documentation</a> and '
49
                        '<a href="%(admin_url)s">change it</a>'
50
                    )
51
                    % {
52
                        "doc_url": doc_url,
53
                        "admin_url": reverse("admin:sites_site_change", args=[site.pk]),
54
                    }
55
                ),
56
            )
57
58
        # Check for missing migrations
59
        doc_url = (
60
            "https://kiwitcms.readthedocs.io/en/latest/"
61
            "installing_docker.html#initial-configuration-of-running-container"
62
        )
63
        executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
64
        plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
65
        if plan:
66
            messages.add_message(
67
                self.request,
68
                messages.ERROR,
69
                mark_safe(  # nosec:B308:B703
70
                    _(
71
                        "You have %(unapplied_migration_count)s unapplied migration(s). "
72
                        'See <a href="%(doc_url)s">documentation</a>'
73
                    )
74
                    % {
75
                        "unapplied_migration_count": len(plan),
76
                        "doc_url": doc_url,
77
                    }
78
                ),
79
            )
80
81
        # List all recent TestPlans and TestRuns
82
        test_plans = (
83
            TestPlan.objects.filter(author=self.request.user)
84
            .order_by("-pk")
85
            .select_related("product", "type")
86
            .annotate(num_runs=Count("run", distinct=True))
87
        )
88
        test_plans_disable_count = test_plans.filter(is_active=False).count()
89
90
        test_runs = (
91
            TestRun.objects.filter(
92
                Q(manager=self.request.user)
93
                | Q(default_tester=self.request.user)
94
                | Q(executions__assignee=self.request.user),
95
                stop_date__isnull=True,
96
            )
97
            .order_by("-pk")
98
            .distinct()
99
        )
100
101
        return {
102
            "test_plans_count": test_plans.count(),
103
            "test_plans_disable_count": test_plans_disable_count,
104
            "last_15_test_plans": test_plans.filter(is_active=True)[:15],
105
            "last_15_test_runs": test_runs[:15],
106
            "test_runs_count": test_runs.count(),
107
        }
108
109
110
@requires_csrf_token
111
def server_error(request):  # pylint: disable=missing-permission-required
112
    """
113
    Render the error page with request object which supports
114
    static URLs so we can load a nice picture.
115
    """
116
    template = loader.get_template("500.html")
117
    return http.HttpResponseServerError(template.render({}, request))
118
119
120
class IterOpen(subprocess.Popen):  # pylint: disable=missing-permission-required
121
    """
122
    Popen which allows us to iterate over the output so we can
123
    stream it back to the browser with some extra eye candy!
124
    """
125
126
    still_waiting = True
127
    has_completed = False
128
129
    @property
130
    def timestamp(self):
131
        return timezone.now().isoformat().replace("T", " ") + " init-db: "
132
133
    def __iter__(self):
134
        os.set_blocking(self.stdout.fileno(), False)
135
        return self
136
137
    def __next__(self):
138
        line = self.stdout.readline()
139
140
        if not line:
141
            if self.still_waiting:
142
                time.sleep(3)
143
                return self.timestamp + "waiting for migrations to start\n"
144
145
            if not self.has_completed:
146
                self.has_completed = True
147
                return self.timestamp + "Complete!\n"
148
149
            # instruct the streaming response to stop streaming
150
            raise StopIteration
151
152
        self.still_waiting = False
153
        return self.timestamp + line.lstrip()
154
155
156
class InitDBView(TemplateView):  # pylint: disable=missing-permission-required
157
158
    template_name = "initdb.html"
159
160
    def post(self, request):
161
        # Default production installation
162
        manage_path = "/Kiwi/manage.py"
163
        if not os.path.exists(manage_path):
164
            # Development installation
165
            manage_path = os.path.join(settings.TCMS_ROOT_PATH, "..", "manage.py")
166
167
        if "init_db" in request.POST:
168
            # Perform migrations
169
            proc = IterOpen(
170
                [manage_path, "migrate"],
171
                stdout=subprocess.PIPE,
172
                stderr=subprocess.STDOUT,
173
                bufsize=1,  # line buffered
174
                universal_newlines=True,
175
            )
176
            response = StreamingHttpResponse(
177
                proc, content_type="text/plain; charset=utf-8"
178
            )
179
            response["Cache-Control"] = "no-cache"
180
            return response
181
182
        return HttpResponseRedirect(reverse("init-db"))
183
184
185
class TranslationMode(View):  # pylint: disable=missing-permission-required
186
    """
187
    Turns on and off translation mode by switching language to
188
    Esperanto!
189
    """
190
191
    @staticmethod
192
    def get_browser_language(request):
193
        """
194
        Returns *ONLY* the language that is sent by the browser via the
195
        Accept-Language headers. Defaults to ``settings.LANGUAGE_CODE`` if
196
        that doesn't work!
197
198
        This is the language we switch back to when translation mode is turned off.
199
200
        Copied from the bottom half of
201
        ``django.utils.translation.trans_real.get_language_from_request()``
202
203
        .. note::
204
205
            Using ``get_language_from_request()`` doesn't work for us because
206
            it first inspects session and cookies and we've already set Esperanto
207
            in both the session and the cookie!
208
        """
209
        accept = request.META.get("HTTP_ACCEPT_LANGUAGE", "")
210
        for accept_lang, _unused in trans_real.parse_accept_lang_header(accept):
211
            if accept_lang == "*":
212
                break
213
214
            if not trans_real.language_code_re.search(accept_lang):
215
                continue
216
217
            try:
218
                return translation.get_supported_language_variant(accept_lang)
219
            except LookupError:
220
                continue
221
222
        try:
223
            return translation.get_supported_language_variant(settings.LANGUAGE_CODE)
224
        except LookupError:
225
            return settings.LANGUAGE_CODE
226
227
    def get(self, request):
228
        """
229
        In the HTML template we'd like to work with simple links
230
        however the view which actually switches the language needs
231
        to be called via POST so we simulate that here!
232
233
        If the URL doesn't explicitly specify language then we turn-off
234
        translation mode by switching back to browser preferred language.
235
        """
236
        browser_lang = self.get_browser_language(request)
237
        post_body = "%s=%s" % (
238
            i18n.LANGUAGE_QUERY_PARAMETER,
239
            request.GET.get(i18n.LANGUAGE_QUERY_PARAMETER, browser_lang),
240
        )
241
        request.META["REQUEST_METHOD"] = "POST"
242
        request.META["CONTENT_LENGTH"] = len(post_body)
243
        request.META["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
244
245
        post_request = request.__class__(request.META)
246
        # pylint: disable=protected-access
247
        post_request._post = http.QueryDict(post_body, encoding=post_request._encoding)
248
249
        return i18n.set_language(post_request)
250