Completed
Push — master ( 9ed73f...7648fa )
by
unknown
01:21
created

StatisticsDetailView.get_context_data()   B

Complexity

Conditions 6

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 35
rs 7.5384
cc 6
1
# coding: utf8
2
3
"""
4
This software is licensed under the Apache 2 license, quoted below.
5
6
Copyright 2014 Crystalnix Limited
7
8
Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
use this file except in compliance with the License. You may obtain a copy of
10
the License at
11
12
    http://www.apache.org/licenses/LICENSE-2.0
13
14
Unless required by applicable law or agreed to in writing, software
15
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
License for the specific language governing permissions and limitations under
18
the License.
19
"""
20
21
import logging
22
import datetime
23
from collections import OrderedDict
24
25
from django.contrib.admin.views.decorators import staff_member_required
26
from django.contrib import messages
27
from django.utils.decorators import method_decorator
28
from django.views.generic import DetailView, ListView, TemplateView
29
from django.views.generic.edit import FormView
30
from django_tables2 import SingleTableView
31
from django.core.urlresolvers import reverse_lazy, reverse
32
from django.shortcuts import get_object_or_404
33
from django.http import Http404
34
from django.utils import timezone
35
from django.core.cache import cache
36
37
from dynamic_preferences.forms import global_preference_form_builder
38
from dynamic_preferences_registry import global_preferences_manager as gpm
39
40
from omaha.statistics import (
41
    get_users_statistics_months,
42
    get_users_statistics_weeks,
43
    get_users_versions,
44
    get_channel_statistics,
45
)
46
from omaha.models import Application, AppRequest
47
from omaha.filters import AppRequestFilter
48
from omaha.utils import make_piechart, make_discrete_bar_chart, get_month_range_from_dict
49
from omaha.filters import EVENT_RESULT, EVENT_TYPE
50
from omaha.tables import AppRequestTable, VersionsTable, VersionsUsageTable
51
from omaha.forms import CrashManualCleanupForm, ManualCleanupForm, MonthRangeForm
52
from omaha.dynamic_preferences_registry import global_preferences
53
54
logger = logging.getLogger(__name__)
55
56
57
STATE_CANCELLED = {
58
    0: 'unknown',
59
    1: 'init',
60
    2: 'waiting to check for update',
61
    3: 'checking for update',
62
    4: 'update available',
63
    5: 'waiting to download',
64
    6: 'retrying download',
65
    7: 'downloading',
66
    8: 'download complete',
67
    9: 'extracting',
68
    10: 'applying differential patch',
69
    11: 'ready to install',
70
    12: 'waiting to install',
71
    13: 'installing',
72
    14: 'install complete',
73
    15: 'paused',
74
    16: 'no update',
75
    17: 'error',
76
}
77
78
79
class StaffMemberRequiredMixin(object):
80
    @method_decorator(staff_member_required)
81
    def dispatch(self, *args, **kwargs):
82
        return super(StaffMemberRequiredMixin, self).dispatch(*args, **kwargs)
83
84
85
class StatisticsView(StaffMemberRequiredMixin, ListView, FormView):
86
    template_name = "admin/omaha/statistics.html"
87
    model = Application
88
    context_object_name = "app_list"
89
    form_class = MonthRangeForm
90
    success_url = reverse_lazy('omaha_statistics')
91
92
    def get_initial(self):
93
        initial = super(StatisticsView, self).get_initial()
94
        initial['start'] = self.request.GET.get('start')
95
        initial['end'] = self.request.GET.get('end')
96
        return initial
97
98
    def get_context_data(self, **kwargs):
99
        context = super(StatisticsView, self).get_context_data(**kwargs)
100
101
        form = self.get_form()
102
        form.is_valid()
103
104
        start, end = get_month_range_from_dict(form.cleaned_data)
105
106
        diapasons = [((start.month if year == start.year else 1, end.month if year == end.year else 12), year)
107
                     for year in range(start.year, end.year+1)]
108
109
        data = []
110
        for diapason in diapasons:
111
            data += get_users_statistics_months(year=diapason[1], start=diapason[0][0], end=diapason[0][1])
112
113
        context['range_start'] = start.strftime('%B %Y')
114
        context['range_end'] = end.strftime('%B %Y')
115
        context['months'] = make_discrete_bar_chart('months', data)
116
        context['weeks'] = make_discrete_bar_chart('weeks', get_users_statistics_weeks())
117
        context['form'] = form
118
        return context
119
120
121
class StatisticsDetailView(StaffMemberRequiredMixin, DetailView, FormView):
122
    model = Application
123
    template_name = 'admin/omaha/statistics_detail.html'
124
    context_object_name = 'app'
125
    form_class = MonthRangeForm
126
    success_url = reverse_lazy('omaha_statistics_detail')
127
128
    def get_object(self, queryset=None):
129
        return get_object_or_404(Application, name=self.kwargs.get('name'))
130
131
    def get_initial(self):
132
        initial = super(StatisticsDetailView, self).get_initial()
133
        initial['start'] = self.request.GET.get('start')
134
        initial['end'] = self.request.GET.get('end')
135
        return initial
136
137
    def get_context_data(self, **kwargs):
138
        context = super(StatisticsDetailView, self).get_context_data(**kwargs)
139
140
        app = self.object
141
        form = self.get_form()
142
        form.is_valid()
143
144
        now = timezone.now()
145
        last_week = now - datetime.timedelta(days=7)
146
147
        start, end = get_month_range_from_dict(form.cleaned_data)
148
149
        diapasons = [((start.month if year == start.year else 1, end.month if year == end.year else 12), year)
150
                     for year in range(start.year, end.year+1)]
151
152
        data = []
153
        for diapason in diapasons:
154
            data += get_users_statistics_months(app_id=app.id, year=diapason[1], start=diapason[0][0], end=diapason[0][1])
155
156
        qs = AppRequest.objects.filter(appid=app.id,
157
                                       request__created__range=[last_week, now])
158
159
        context['range_start'] = start.strftime('%B %Y')
160
        context['range_end'] = end.strftime('%B %Y')
161
        context['install_count'] = qs.filter(events__eventtype=2).count()
162
        context['update_count'] = qs.filter(events__eventtype=3).count()
163
164
        context['months'] = make_discrete_bar_chart('months', data)
165
        context['weeks'] = make_discrete_bar_chart('weeks', get_users_statistics_weeks(app_id=app.id))
166
        context['versions'] = make_piechart('versions', get_users_versions(app.id))
167
        context['channels'] = make_piechart('channels', get_channel_statistics(app.id))
168
        versions_data = [dict(version=x[0], number=x[1]) for x in get_users_versions(app.id)]
169
        context['versions_table'] = VersionsTable(versions_data)
170
        context['form'] = form
171
        return context
172
173
174
class LiveStatisticsView(StaffMemberRequiredMixin, DetailView):
175
    model = Application
176
    template_name = 'admin/omaha/live_statistics.html'
177
    context_object_name = 'app'
178
179
    def get_object(self, queryset=None):
180
        return get_object_or_404(Application, name=self.kwargs.get('name'))
181
182
    def get_context_data(self, **kwargs):
183
        context = super(LiveStatisticsView, self).get_context_data(**kwargs)
184
185
        app = self.object
186
187
        context['app_name'] = app.name
188
        return context
189
190
191
class VersionsUsageView(StaffMemberRequiredMixin, SingleTableView):
192
    model = AppRequest
193
    template_name = 'admin/omaha/version_usage.html'
194
    context_object_name = 'version_usage'
195
    table_class = VersionsUsageTable
196
197
    def get_queryset(self):
198
        qs = super(VersionsUsageView, self).get_queryset()
199
200
        qs = qs.select_related('request', 'request__os')
201
        qs = qs.order_by('-request__created')
202
        self.appid = None
203
204
        try:
205
            app = Application.objects.get(name=self.kwargs.get('name'))
206
            qs = qs.filter(appid=app.id)
207
            self.appid = app.id
208
        except Application.DoesNotExist:
209
            raise Http404
210
211
        qs = qs.filter(events__eventtype__in=[2, 3], events__eventresult=1)
212
        qs = qs.distinct('request__userid').order_by('request__userid', '-request__created')
213
        return list(qs)
214
215
    def get_context_data(self, **kwargs):
216
        context = super(VersionsUsageView, self).get_context_data(**kwargs)
217
        context['app_name'] = self.kwargs.get('name')
218
        return context
219
220
221
class RequestListView(StaffMemberRequiredMixin, SingleTableView):
222
    model = AppRequest
223
    context_object_name = 'request_list'
224
    template_name = 'admin/omaha/request_list.html'
225
    table_class = AppRequestTable
226
227
    def get_queryset(self):
228
        qs = super(RequestListView, self).get_queryset()
229
        qs = qs.select_related('request', 'request__os',)
230
        qs = qs.prefetch_related('events')
231
        qs = qs.order_by('-request__created')
232
        self.appid = None
233
234
        try:
235
            app = Application.objects.get(name=self.kwargs.get('name'))
236
            qs = qs.filter(appid=app.id)
237
            self.appid = app.id
238
        except Application.DoesNotExist:
239
            raise Http404
240
241
        qs = qs.distinct()
242
        self.filter = AppRequestFilter(self.request.GET, queryset=qs)
243
        return self.filter.qs
244
245
    def get_context_data(self, **kwargs):
246
        context = super(RequestListView, self).get_context_data(**kwargs)
247
        context['filter'] = self.filter
248
        context['app_name'] = self.kwargs.get('name')
249
        context['app_id'] = self.appid
250
        return context
251
252
253
class AppRequestDetailView(StaffMemberRequiredMixin, DetailView):
254
    model = AppRequest
255
    template_name = 'admin/omaha/request_detail.html'
256
257
    def get_queryset(self):
258
        qs = super(AppRequestDetailView, self).get_queryset()
259
        qs = qs.select_related('request', 'request__os', 'request__hw')
260
        qs = qs.prefetch_related('events')
261
        return qs
262
263
    def get_context_data(self, **kwargs):
264
        name = 'Unknown'
265
        try:
266
            app_req = self.get_object()
267
            app = Application.objects.get(id=app_req.appid)
268
            name = app.name
269
        except Application.DoesNotExist:
270
            logger.error('AppRequestDetailView DoesNotExist', exc_info=True, extra=dict(request=self.request))
271
        context = super(AppRequestDetailView, self).get_context_data(**kwargs)
272
        context['app_name'] = name
273
        context['EVENT_RESULT'] = EVENT_RESULT
274
        context['EVENT_TYPE'] = EVENT_TYPE
275
        context['STATE_CANCELLED'] = STATE_CANCELLED
276
        return context
277
278
279
class PreferenceFormView(StaffMemberRequiredMixin, FormView):
280
    template_name = 'admin/omaha/set_preferences.html'
281
    registry = global_preferences
282
283
    def get_success_url(self):
284
        return self.request.path
285
286
    def get_form_class(self, *args, **kwargs):
287
        section = self.kwargs.get('section', None)
288
        form_class = global_preference_form_builder(section=section)
289
        return form_class
290
291
    def get_context_data(self, *args, **kwargs):
292
        context = super(PreferenceFormView, self).get_context_data(*args, **kwargs)
293
        context['sections'] = self.registry.sections()
294
        context['sections'].sort()
295
        context['cur_section'] = self.kwargs.get('section')
296
        form = context['form']
297
        order_fields = ['Crash__limit_size', 'Crash__limit_storage_days', 'Crash__duplicate_number',
298
                        'Feedback__limit_size', 'Feedback__limit_storage_days', 'SparkleVersion__limit_size',
299
                        'Symbols__limit_size', 'Version__limit_size', 'Timezone__timezone']
300
        form.fields = OrderedDict((k, form.fields[k]) for k in order_fields if k in form.fields.keys())
301
        return context
302
303
    def form_valid(self, form):
304
        form.update_preferences()
305
        try:
306
            self.request.session['django_timezone'] = form.cleaned_data['Timezone__timezone']
307
        except KeyError:
308
            pass
309
        messages.add_message(self.request, messages.INFO, 'Preferences were updated')
310
        return super(PreferenceFormView, self).form_valid(form)
311
312
313
class MonitoringFormView(StaffMemberRequiredMixin, TemplateView):
314
    template_name = 'admin/omaha/monitoring.html'
315
    form_class = ManualCleanupForm
316
    success_url = reverse_lazy('monitoring')
317
318
    def get_context_data(self, **kwargs):
319
        context = super(MonitoringFormView, self).get_context_data(**kwargs)
320
        omaha_version_size = float(cache.get('omaha_version_size') or 0)/1073741824
321
        sparkle_version_size = float(cache.get('sparkle_version_size') or 0)/1073741824
322
        feedbacks_size = float(cache.get('feedbacks_size') or 0)/1073741824
323
        crashes_size = float(cache.get('crashes_size') or 0)/1073741824
324
        symbols_size = float(cache.get('symbols_size') or 0)/1073741824
325
326
        data = dict(
327
            omaha_version=dict(label='Omaha Versions', size=omaha_version_size, limit=gpm['Version__limit_size'], percent=omaha_version_size/gpm['Version__limit_size']*100),
328
            sparkle_version=dict(label='Sparkle Versions', size=sparkle_version_size, limit=gpm['SparkleVersion__limit_size'], percent=sparkle_version_size/gpm['SparkleVersion__limit_size']*100),
329
            feedbacks=dict(label='Feedbacks', size=feedbacks_size, limit=gpm['Feedback__limit_size'], percent=feedbacks_size/gpm['Feedback__limit_size']*100),
330
            crashes=dict(label='Crashes',  size=crashes_size, limit=gpm['Crash__limit_size'], percent=crashes_size/gpm['Crash__limit_size']*100),
331
            symbols=dict(label='Symbols',  size=symbols_size, limit=gpm['Symbols__limit_size'], percent=symbols_size/gpm['Symbols__limit_size']*100),
332
        )
333
        full_size = reduce(lambda res, x: res + x['size'], data.values(), 0)
334
        context.update(data)
335
        piechart = None
336
        if full_size:
337
            pie_data = [(x['label'], x['size']/full_size * 100) for x in data.values()]
338
            piechart = make_piechart('used_space', pie_data, unit="%")
339
        context.update({'used_space': piechart})
340
        return context
341
342
343
class ManualCleanupFormView(StaffMemberRequiredMixin, FormView):
344
    template_name = 'admin/omaha/manually_deletion.html'
345
    form_class = CrashManualCleanupForm
346
347
    def get_context_data(self, **kwargs):
348
        context = super(ManualCleanupFormView, self).get_context_data(**kwargs)
349
        context['tabs'] = (
350
            ('crash__Crash', 'Crash'),
351
            ('feedback__Feedback', 'Feedback'),
352
            ('omaha__Version', 'Omaha Version'),
353
            ('sparkle__SparkleVersion', 'Sparkle Version'),
354
            ('crash__Symbols', 'Symbols')
355
        )
356
        context['cur_tab'] = self.kwargs.get('model')
357
        return context
358
359
    def get_success_url(self):
360
        return reverse('monitoring')
361
362
    def get_initial(self):
363
        return {'type': self.kwargs.get('model')}
364
365
    def get_form_class(self):
366
        cur_tab = self.kwargs.get('model')
367
        if cur_tab == 'crash__Crash':
368
            return CrashManualCleanupForm
369
        if cur_tab in ('feedback__Feedback', 'omaha__Version', 'sparkle__SparkleVersion', 'crash__Symbols'):
370
            return ManualCleanupForm
371
        raise Http404
372
373
    def form_valid(self, form):
374
        form.cleanup()
375
        messages.add_message(self.request, messages.INFO, 'Task was added in queue. Execution can take a long time. Check results on Sentry after a while.')
376
        return super(ManualCleanupFormView, self).form_valid(form)
377