Completed
Push — master ( 67eae9...f0f1ec )
by
unknown
01:23
created

StatisticsDetailView   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 21
Duplicated Lines 0 %
Metric Value
wmc 2
dl 0
loc 21
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A get_context_data() 0 12 1
A get_object() 0 2 1
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
import pytz
24
from collections import OrderedDict
25
26
from django.contrib.admin.views.decorators import staff_member_required
27
from django.contrib import messages
28
from django.utils.decorators import method_decorator
29
from django.views.generic import DetailView, ListView, TemplateView
30
from django.views.generic.edit import FormView
31
from django_tables2 import SingleTableView
32
from django.core.urlresolvers import reverse_lazy, reverse
33
from django.shortcuts import get_object_or_404
34
from django.http import Http404
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.models import Application, AppRequest, Version
41
from omaha.filters import AppRequestFilter
42
from omaha.utils import make_piechart
43
from omaha.filters import EVENT_RESULT, EVENT_TYPE
44
from omaha.tables import AppRequestTable, VersionsUsageTable
45
from omaha.forms import CrashManualCleanupForm, ManualCleanupForm
46
from omaha.dynamic_preferences_registry import global_preferences
47
from sparkle.models import SparkleVersion
48
49
logger = logging.getLogger(__name__)
50
51
52
STATE_CANCELLED = {
53
    0: 'unknown',
54
    1: 'init',
55
    2: 'waiting to check for update',
56
    3: 'checking for update',
57
    4: 'update available',
58
    5: 'waiting to download',
59
    6: 'retrying download',
60
    7: 'downloading',
61
    8: 'download complete',
62
    9: 'extracting',
63
    10: 'applying differential patch',
64
    11: 'ready to install',
65
    12: 'waiting to install',
66
    13: 'installing',
67
    14: 'install complete',
68
    15: 'paused',
69
    16: 'no update',
70
    17: 'error',
71
}
72
73
74
class StaffMemberRequiredMixin(object):
75
    @method_decorator(staff_member_required)
76
    def dispatch(self, *args, **kwargs):
77
        return super(StaffMemberRequiredMixin, self).dispatch(*args, **kwargs)
78
79
80
class StatisticsView(StaffMemberRequiredMixin, ListView):
81
    template_name = "admin/omaha/statistics.html"
82
    model = Application
83
    context_object_name = "app_list"
84
85
86
class StatisticsDetailView(StaffMemberRequiredMixin, DetailView):
87
    model = Application
88
    template_name = 'admin/omaha/statistics_detail.html'
89
    context_object_name = 'app'
90
    success_url = reverse_lazy('omaha_statistics_detail')
91
92
    def get_object(self, queryset=None):
93
        return get_object_or_404(Application, name=self.kwargs.get('name'))
94
95
    def get_context_data(self, **kwargs):
96
        context = super(StatisticsDetailView, self).get_context_data(**kwargs)
97
98
        app = self.object
99
100
        first_win_version = Version.objects.all().filter(app=app).order_by('created').first()
101
        win_start_date = getattr(first_win_version, 'created', datetime.datetime(1970, 1, 1, tzinfo=pytz.UTC))
102
103
        first_mac_version = SparkleVersion.objects.all().filter(app=app).order_by('created').first()
104
        mac_start_date = getattr(first_mac_version, 'created', datetime.datetime(1970, 1, 1, tzinfo=pytz.UTC))
105
        context['start_date'] = min(win_start_date, mac_start_date).isoformat()
106
        return context
107
108
109
class LiveStatisticsView(StaffMemberRequiredMixin, DetailView):
110
    model = Application
111
    template_name = 'admin/omaha/live_statistics.html'
112
    context_object_name = 'app'
113
114
    def get_object(self, queryset=None):
115
        return get_object_or_404(Application, name=self.kwargs.get('name'))
116
117
    def get_context_data(self, **kwargs):
118
        context = super(LiveStatisticsView, self).get_context_data(**kwargs)
119
120
        app = self.object
121
122
        context['app_name'] = app.name
123
        return context
124
125
126
class VersionsUsageView(StaffMemberRequiredMixin, SingleTableView):
127
    model = AppRequest
128
    template_name = 'admin/omaha/version_usage.html'
129
    context_object_name = 'version_usage'
130
    table_class = VersionsUsageTable
131
132
    def get_queryset(self):
133
        qs = super(VersionsUsageView, self).get_queryset()
134
135
        qs = qs.select_related('request', 'request__os')
136
        qs = qs.order_by('-request__created')
137
        self.appid = None
138
139
        try:
140
            app = Application.objects.get(name=self.kwargs.get('name'))
141
            qs = qs.filter(appid=app.id)
142
            self.appid = app.id
143
        except Application.DoesNotExist:
144
            raise Http404
145
146
        qs = qs.filter(events__eventtype__in=[2, 3], events__eventresult=1)
147
        qs = qs.distinct('request__userid').order_by('request__userid', '-request__created')
148
        return list(qs)
149
150
    def get_context_data(self, **kwargs):
151
        context = super(VersionsUsageView, self).get_context_data(**kwargs)
152
        context['app_name'] = self.kwargs.get('name')
153
        return context
154
155
156
class RequestListView(StaffMemberRequiredMixin, SingleTableView):
157
    model = AppRequest
158
    context_object_name = 'request_list'
159
    template_name = 'admin/omaha/request_list.html'
160
    table_class = AppRequestTable
161
162
    def get_queryset(self):
163
        qs = super(RequestListView, self).get_queryset()
164
        qs = qs.select_related('request', 'request__os',)
165
        qs = qs.prefetch_related('events')
166
        qs = qs.order_by('-request__created')
167
        self.appid = None
168
169
        try:
170
            app = Application.objects.get(name=self.kwargs.get('name'))
171
            qs = qs.filter(appid=app.id)
172
            self.appid = app.id
173
        except Application.DoesNotExist:
174
            raise Http404
175
176
        qs = qs.distinct()
177
        self.filter = AppRequestFilter(self.request.GET, queryset=qs)
178
        return self.filter.qs
179
180
    def get_context_data(self, **kwargs):
181
        context = super(RequestListView, self).get_context_data(**kwargs)
182
        context['filter'] = self.filter
183
        context['app_name'] = self.kwargs.get('name')
184
        context['app_id'] = self.appid
185
        return context
186
187
188
class AppRequestDetailView(StaffMemberRequiredMixin, DetailView):
189
    model = AppRequest
190
    template_name = 'admin/omaha/request_detail.html'
191
192
    def get_queryset(self):
193
        qs = super(AppRequestDetailView, self).get_queryset()
194
        qs = qs.select_related('request', 'request__os', 'request__hw')
195
        qs = qs.prefetch_related('events')
196
        return qs
197
198
    def get_context_data(self, **kwargs):
199
        name = 'Unknown'
200
        try:
201
            app_req = self.get_object()
202
            app = Application.objects.get(id=app_req.appid)
203
            name = app.name
204
        except Application.DoesNotExist:
205
            logger.error('AppRequestDetailView DoesNotExist', exc_info=True, extra=dict(request=self.request))
206
        context = super(AppRequestDetailView, self).get_context_data(**kwargs)
207
        context['app_name'] = name
208
        context['EVENT_RESULT'] = EVENT_RESULT
209
        context['EVENT_TYPE'] = EVENT_TYPE
210
        context['STATE_CANCELLED'] = STATE_CANCELLED
211
        return context
212
213
214
class PreferenceFormView(StaffMemberRequiredMixin, FormView):
215
    template_name = 'admin/omaha/set_preferences.html'
216
    registry = global_preferences
217
218
    def get_success_url(self):
219
        return self.request.path
220
221
    def get_form_class(self, *args, **kwargs):
222
        section = self.kwargs.get('section', None)
223
        form_class = global_preference_form_builder(section=section)
224
        return form_class
225
226
    def get_context_data(self, *args, **kwargs):
227
        context = super(PreferenceFormView, self).get_context_data(*args, **kwargs)
228
        context['sections'] = self.registry.sections()
229
        context['sections'].sort()
230
        context['cur_section'] = self.kwargs.get('section')
231
        form = context['form']
232
        order_fields = ['Crash__limit_size', 'Crash__limit_storage_days', 'Crash__duplicate_number',
233
                        'Feedback__limit_size', 'Feedback__limit_storage_days', 'SparkleVersion__limit_size',
234
                        'Symbols__limit_size', 'Version__limit_size', 'Timezone__timezone']
235
        form.fields = OrderedDict((k, form.fields[k]) for k in order_fields if k in form.fields.keys())
236
        return context
237
238
    def form_valid(self, form):
239
        form.update_preferences()
240
        try:
241
            self.request.session['django_timezone'] = form.cleaned_data['Timezone__timezone']
242
        except KeyError:
243
            pass
244
        messages.add_message(self.request, messages.INFO, 'Preferences were updated')
245
        return super(PreferenceFormView, self).form_valid(form)
246
247
248
class MonitoringFormView(StaffMemberRequiredMixin, TemplateView):
249
    template_name = 'admin/omaha/monitoring.html'
250
    form_class = ManualCleanupForm
251
    success_url = reverse_lazy('monitoring')
252
253
    def get_context_data(self, **kwargs):
254
        context = super(MonitoringFormView, self).get_context_data(**kwargs)
255
        omaha_version_size = float(cache.get('omaha_version_size') or 0)/1073741824
256
        sparkle_version_size = float(cache.get('sparkle_version_size') or 0)/1073741824
257
        feedbacks_size = float(cache.get('feedbacks_size') or 0)/1073741824
258
        crashes_size = float(cache.get('crashes_size') or 0)/1073741824
259
        symbols_size = float(cache.get('symbols_size') or 0)/1073741824
260
261
        data = dict(
262
            omaha_version=dict(label='Omaha Versions', size=omaha_version_size, limit=gpm['Version__limit_size'], percent=omaha_version_size/gpm['Version__limit_size']*100),
263
            sparkle_version=dict(label='Sparkle Versions', size=sparkle_version_size, limit=gpm['SparkleVersion__limit_size'], percent=sparkle_version_size/gpm['SparkleVersion__limit_size']*100),
264
            feedbacks=dict(label='Feedbacks', size=feedbacks_size, limit=gpm['Feedback__limit_size'], percent=feedbacks_size/gpm['Feedback__limit_size']*100),
265
            crashes=dict(label='Crashes',  size=crashes_size, limit=gpm['Crash__limit_size'], percent=crashes_size/gpm['Crash__limit_size']*100),
266
            symbols=dict(label='Symbols',  size=symbols_size, limit=gpm['Symbols__limit_size'], percent=symbols_size/gpm['Symbols__limit_size']*100),
267
        )
268
        full_size = reduce(lambda res, x: res + x['size'], data.values(), 0)
269
        context.update(data)
270
        piechart = None
271
        if full_size:
272
            pie_data = [(x['label'], x['size']/full_size * 100) for x in data.values()]
273
            piechart = make_piechart('used_space', pie_data, unit="%")
274
        context.update({'used_space': piechart})
275
        return context
276
277
278
class ManualCleanupFormView(StaffMemberRequiredMixin, FormView):
279
    template_name = 'admin/omaha/manually_deletion.html'
280
    form_class = CrashManualCleanupForm
281
282
    def get_context_data(self, **kwargs):
283
        context = super(ManualCleanupFormView, self).get_context_data(**kwargs)
284
        context['tabs'] = (
285
            ('crash__Crash', 'Crash'),
286
            ('feedback__Feedback', 'Feedback'),
287
            ('omaha__Version', 'Omaha Version'),
288
            ('sparkle__SparkleVersion', 'Sparkle Version'),
289
            ('crash__Symbols', 'Symbols')
290
        )
291
        context['cur_tab'] = self.kwargs.get('model')
292
        return context
293
294
    def get_success_url(self):
295
        return reverse('monitoring')
296
297
    def get_initial(self):
298
        return {'type': self.kwargs.get('model')}
299
300
    def get_form_class(self):
301
        cur_tab = self.kwargs.get('model')
302
        if cur_tab == 'crash__Crash':
303
            return CrashManualCleanupForm
304
        if cur_tab in ('feedback__Feedback', 'omaha__Version', 'sparkle__SparkleVersion', 'crash__Symbols'):
305
            return ManualCleanupForm
306
        raise Http404
307
308
    def form_valid(self, form):
309
        form.cleanup()
310
        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.')
311
        return super(ManualCleanupFormView, self).form_valid(form)
312