Completed
Push — master ( 3d6838...745652 )
by Egor
01:09
created

VersionsUsageView.get_queryset()   A

Complexity

Conditions 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 17
rs 9.4285
cc 2
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
49
from omaha.filters import EVENT_RESULT, EVENT_TYPE
50
from omaha.tables import AppRequestTable, VersionsTable, VersionsUsageTable
51
from omaha.forms import CrashManualCleanupForm, ManualCleanupForm
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):
86
    template_name = "admin/omaha/statistics.html"
87
    model = Application
88
    context_object_name = "app_list"
89
90
    def get_context_data(self, **kwargs):
91
        context = super(StatisticsView, self).get_context_data(**kwargs)
92
93
        context['months'] = make_discrete_bar_chart('months', get_users_statistics_months())
94
        context['weeks'] = make_discrete_bar_chart('weeks', get_users_statistics_weeks())
95
96
        return context
97
98
99
class StatisticsDetailView(StaffMemberRequiredMixin, DetailView):
100
    model = Application
101
    template_name = 'admin/omaha/statistics_detail.html'
102
    context_object_name = 'app'
103
104
    def get_object(self, queryset=None):
105
        return get_object_or_404(Application, name=self.kwargs.get('name'))
106
107
    def get_context_data(self, **kwargs):
108
        context = super(StatisticsDetailView, self).get_context_data(**kwargs)
109
110
        app = self.object
111
112
        now = timezone.now()
113
        last_week = now - datetime.timedelta(days=7)
114
115
        qs = AppRequest.objects.filter(appid=app.id,
116
                                       request__created__range=[last_week, now])
117
118
        context['install_count'] = qs.filter(events__eventtype=2).count()
119
        context['update_count'] = qs.filter(events__eventtype=3).count()
120
121
        context['months'] = make_discrete_bar_chart('months', get_users_statistics_months(app_id=app.id))
122
        context['weeks'] = make_discrete_bar_chart('weeks', get_users_statistics_weeks(app_id=app.id))
123
        context['versions'] = make_piechart('versions', get_users_versions(app.id))
124
        context['channels'] = make_piechart('channels', get_channel_statistics(app.id))
125
        versions_data = [dict(version=x[0], number=x[1]) for x in get_users_versions(app.id)]
126
        context['versions_table'] = VersionsTable(versions_data)
127
        return context
128
129
130
class VersionsUsageView(StaffMemberRequiredMixin, SingleTableView):
131
    model = AppRequest
132
    template_name = 'admin/omaha/version_usage.html'
133
    context_object_name = 'version_usage'
134
    table_class = VersionsUsageTable
135
136
    def get_queryset(self):
137
        qs = super(VersionsUsageView, self).get_queryset()
138
139
        qs = qs.select_related('request', 'request__os')
140
        qs = qs.order_by('-request__created')
141
        self.appid = None
142
143
        try:
144
            app = Application.objects.get(name=self.kwargs.get('name'))
145
            qs = qs.filter(appid=app.id)
146
            self.appid = app.id
147
        except Application.DoesNotExist:
148
            raise Http404
149
150
        qs = qs.filter(events__eventtype__in=[2, 3], events__eventresult=1)
151
        qs = qs.distinct('request__userid').order_by('request__userid', '-request__created')
152
        return list(qs)
153
154
    def get_context_data(self, **kwargs):
155
        context = super(VersionsUsageView, self).get_context_data(**kwargs)
156
        context['app_name'] = self.kwargs.get('name')
157
        return context
158
159
160
class RequestListView(StaffMemberRequiredMixin, SingleTableView):
161
    model = AppRequest
162
    context_object_name = 'request_list'
163
    template_name = 'admin/omaha/request_list.html'
164
    table_class = AppRequestTable
165
166
    def get_queryset(self):
167
        qs = super(RequestListView, self).get_queryset()
168
        qs = qs.select_related('request', 'request__os',)
169
        qs = qs.prefetch_related('events')
170
        qs = qs.order_by('-request__created')
171
        self.appid = None
172
173
        try:
174
            app = Application.objects.get(name=self.kwargs.get('name'))
175
            qs = qs.filter(appid=app.id)
176
            self.appid = app.id
177
        except Application.DoesNotExist:
178
            raise Http404
179
180
        qs = qs.distinct()
181
        self.filter = AppRequestFilter(self.request.GET, queryset=qs)
182
        return self.filter.qs
183
184
    def get_context_data(self, **kwargs):
185
        context = super(RequestListView, self).get_context_data(**kwargs)
186
        context['filter'] = self.filter
187
        context['app_name'] = self.kwargs.get('name')
188
        context['app_id'] = self.appid
189
        return context
190
191
192
class AppRequestDetailView(StaffMemberRequiredMixin, DetailView):
193
    model = AppRequest
194
    template_name = 'admin/omaha/request_detail.html'
195
196
    def get_queryset(self):
197
        qs = super(AppRequestDetailView, self).get_queryset()
198
        qs = qs.select_related('request', 'request__os', 'request__hw')
199
        qs = qs.prefetch_related('events')
200
        return qs
201
202
    def get_context_data(self, **kwargs):
203
        name = 'Unknown'
204
        try:
205
            app_req = self.get_object()
206
            app = Application.objects.get(id=app_req.appid)
207
            name = app.name
208
        except Application.DoesNotExist:
209
            logger.error('AppRequestDetailView DoesNotExist', exc_info=True, extra=dict(request=self.request))
210
        context = super(AppRequestDetailView, self).get_context_data(**kwargs)
211
        context['app_name'] = name
212
        context['EVENT_RESULT'] = EVENT_RESULT
213
        context['EVENT_TYPE'] = EVENT_TYPE
214
        context['STATE_CANCELLED'] = STATE_CANCELLED
215
        return context
216
217
218
class PreferenceFormView(StaffMemberRequiredMixin, FormView):
219
    template_name = 'admin/omaha/set_preferences.html'
220
    registry = global_preferences
221
222
    def get_success_url(self):
223
        return self.request.path
224
225
    def get_form_class(self, *args, **kwargs):
226
        section = self.kwargs.get('section', None)
227
        form_class = global_preference_form_builder(section=section)
228
        return form_class
229
230
    def get_context_data(self, *args, **kwargs):
231
        context = super(PreferenceFormView, self).get_context_data(*args, **kwargs)
232
        context['sections'] = self.registry.sections()
233
        context['sections'].sort()
234
        context['cur_section'] = self.kwargs.get('section')
235
        form = context['form']
236
        order_fields = ['Crash__limit_size', 'Crash__limit_storage_days', 'Crash__duplicate_number',
237
                        'Feedback__limit_size', 'Feedback__limit_storage_days', 'SparkleVersion__limit_size',
238
                        'Symbols__limit_size', 'Version__limit_size', 'Timezone__timezone']
239
        form.fields = OrderedDict((k, form.fields[k]) for k in order_fields if k in form.fields.keys())
240
        return context
241
242
    def form_valid(self, form):
243
        form.update_preferences()
244
        try:
245
            self.request.session['django_timezone'] = form.cleaned_data['Timezone__timezone']
246
        except KeyError:
247
            pass
248
        messages.add_message(self.request, messages.INFO, 'Preferences were updated')
249
        return super(PreferenceFormView, self).form_valid(form)
250
251
252
class MonitoringFormView(StaffMemberRequiredMixin, TemplateView):
253
    template_name = 'admin/omaha/monitoring.html'
254
    form_class = ManualCleanupForm
255
    success_url = reverse_lazy('monitoring')
256
257
    def get_context_data(self, **kwargs):
258
        context = super(MonitoringFormView, self).get_context_data(**kwargs)
259
        omaha_version_size = float(cache.get('omaha_version_size') or 0)/1073741824
260
        sparkle_version_size = float(cache.get('sparkle_version_size') or 0)/1073741824
261
        feedbacks_size = float(cache.get('feedbacks_size') or 0)/1073741824
262
        crashes_size = float(cache.get('crashes_size') or 0)/1073741824
263
        symbols_size = float(cache.get('symbols_size') or 0)/1073741824
264
265
        data = dict(
266
            omaha_version=dict(label='Omaha Versions', size=omaha_version_size, limit=gpm['Version__limit_size'], percent=omaha_version_size/gpm['Version__limit_size']*100),
267
            sparkle_version=dict(label='Sparkle Versions', size=sparkle_version_size, limit=gpm['SparkleVersion__limit_size'], percent=sparkle_version_size/gpm['SparkleVersion__limit_size']*100),
268
            feedbacks=dict(label='Feedbacks', size=feedbacks_size, limit=gpm['Feedback__limit_size'], percent=feedbacks_size/gpm['Feedback__limit_size']*100),
269
            crashes=dict(label='Crashes',  size=crashes_size, limit=gpm['Crash__limit_size'], percent=crashes_size/gpm['Crash__limit_size']*100),
270
            symbols=dict(label='Symbols',  size=symbols_size, limit=gpm['Symbols__limit_size'], percent=symbols_size/gpm['Symbols__limit_size']*100),
271
        )
272
        full_size = reduce(lambda res, x: res + x['size'], data.values(), 0)
273
        context.update(data)
274
        piechart = None
275
        if full_size:
276
            pie_data = [(x['label'], x['size']/full_size * 100) for x in data.values()]
277
            piechart = make_piechart('used_space', pie_data, unit="%")
278
        context.update({'used_space': piechart})
279
        return context
280
281
282
class ManualCleanupFormView(StaffMemberRequiredMixin, FormView):
283
    template_name = 'admin/omaha/manually_deletion.html'
284
    form_class = CrashManualCleanupForm
285
286
    def get_context_data(self, **kwargs):
287
        context = super(ManualCleanupFormView, self).get_context_data(**kwargs)
288
        context['tabs'] = (
289
            ('crash__Crash', 'Crash'),
290
            ('feedback__Feedback', 'Feedback'),
291
            ('omaha__Version', 'Omaha Version'),
292
            ('sparkle__SparkleVersion', 'Sparkle Version'),
293
            ('crash__Symbols', 'Symbols')
294
        )
295
        context['cur_tab'] = self.kwargs.get('model')
296
        return context
297
298
    def get_success_url(self):
299
        return reverse('monitoring')
300
301
    def get_initial(self):
302
        return {'type': self.kwargs.get('model')}
303
304
    def get_form_class(self):
305
        cur_tab = self.kwargs.get('model')
306
        if cur_tab == 'crash__Crash':
307
            return CrashManualCleanupForm
308
        if cur_tab in ('feedback__Feedback', 'omaha__Version', 'sparkle__SparkleVersion', 'crash__Symbols'):
309
            return ManualCleanupForm
310
        raise Http404
311
312
    def form_valid(self, form):
313
        form.cleanup()
314
        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.')
315
        return super(ManualCleanupFormView, self).form_valid(form)
316