Completed
Push — master ( 6c4ac7...836a98 )
by
unknown
01:30
created

build_event_name()   A

Complexity

Conditions 2

Size

Total Lines 5

Duplication

Lines 5
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 5
loc 5
rs 9.4285
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
from builtins import range
22
23
from functools import partial
24
from datetime import datetime, timedelta
25
26
from django.conf import settings
27
from django.db import transaction
28
from django.utils import timezone
29
from bitmapist import setup_redis, mark_event, unmark_event, WeekEvents, MonthEvents, DayEvents, HourEvents
30
import pytz
31
32
from omaha.utils import get_id, is_new_install, valuedispatch, redis
33
from omaha import parser
34
from omaha.models import ACTIVE_USERS_DICT_CHOICES, Request, AppRequest, Os, Hw, Event, Version, Channel
35
from sparkle.models import SparkleVersion
36
37
__all__ = ['userid_counting', 'is_user_active']
38
39
host, port, db = settings.CACHES['statistics']['LOCATION'].split(':')
40
setup_redis('default', host, port, db=db)
41
42
43
def userid_counting(userid, apps_list, platform, now=None):
44
    id = get_id(userid)
45
    mark_event('request', id, now=now)
46
    list(map(partial(add_app_statistics, id, platform, now=now), apps_list or []))
47
48
49
def add_app_statistics(userid, platform, app, now=None):
50
    mark = partial(mark_event, now=now)
51
    if not now:
52
        now = timezone.now()
53
    appid = app.get('appid')
54
    version = app.get('version')
55
    channel = parser.get_channel(app)
56
    events = app.findall('event')
57
    nextversion = app.get('nextversion')
58
59
    err_events = filter(lambda x: x.get('eventresult') not in ['1', '2', '3'], events)
60
    if err_events:
61
        return
62
63
    install_event = filter(lambda x: x.get('eventtype') == '2', events)
64
    if is_new_install(appid, userid):
65
        if install_event:
66
            mark('new_install:%s' % appid, userid)
67
            mark('new_install:{}:{}'.format(appid, platform), userid)
68
            redis.setbit("known_users:%s" % appid, userid, 1)
69
            mark('request:{}:{}'.format(appid, nextversion), userid, track_hourly=True)
70
            mark('request:{}:{}:{}'.format(appid, platform, nextversion), userid, track_hourly=True)
71
            mark('request:{}:{}:{}:{}'.format(appid, platform, channel, nextversion), userid, track_hourly=True)
72
            mark('request:{}:{}'.format(appid, channel), userid)
73
            return
74
75
    elif userid not in MonthEvents('new_install:{}:{}'.format(appid, platform), year=now.year, month=now.month):
76
        mark('request:%s' % appid, userid)
77
        mark('request:{}:{}'.format(appid, platform), userid)
78
        if nextversion:
79
            mark('request:{}:{}'.format(appid, nextversion), userid, track_hourly=True)
80
            mark('request:{}:{}:{}'.format(appid, platform, nextversion), userid, track_hourly=True)
81
            mark('request:{}:{}:{}:{}'.format(appid, platform, channel, nextversion), userid, track_hourly=True)
82
83
    uninstall_event = filter(lambda x: x.get('eventtype') == '4', events)
84
    if uninstall_event:
85
        mark('uninstall:%s' % appid, userid)
86
        mark('uninstall:{}:{}'.format(appid, platform), userid)
87
    update_event = filter(lambda x: x.get('eventtype') == '3', events)
88
    if update_event:
89
        unmark_event('request:{}:{}'.format(appid, version), userid, track_hourly=True)
90
        unmark_event('request:{}:{}:{}'.format(appid, platform, version), userid, track_hourly=True)
91
        unmark_event('request:{}:{}:{}:{}'.format(appid, platform, channel, version), userid, track_hourly=True)
92
        mark('request:{}:{}'.format(appid, nextversion), userid, track_hourly=True)
93
        mark('request:{}:{}:{}'.format(appid, platform, nextversion), userid, track_hourly=True)
94
        mark('request:{}:{}:{}:{}'.format(appid, platform, channel, nextversion), userid, track_hourly=True)
95
    else:
96
        mark('request:{}:{}'.format(appid, version), userid, track_hourly=True)
97
        mark('request:{}:{}:{}'.format(appid, platform, version), userid, track_hourly=True)
98
        mark('request:{}:{}:{}:{}'.format(appid, platform, channel, version), userid, track_hourly=True)
99
    mark('request:{}:{}'.format(appid, channel), userid)
100
101
102
def get_users_statistics_months(app_id, platform=None, year=None, start=1, end=12):
103
    now = timezone.now()
104
    if not year:
105
        year = now.year
106
107
    if platform:
108
        install_event_name = 'new_install:{}:{}'.format(app_id, platform)
109
        update_event_name = 'request:{}:{}'.format(app_id, platform)
110
        uninstall_event_name = 'uninstall:{}:{}'.format(app_id, platform)
111
    else:
112
        install_event_name = 'new_install:%s' % app_id
113
        update_event_name = 'request:%s' % app_id
114
        uninstall_event_name = 'uninstall:%s' % app_id
115
116
    installs_by_month = []
117
    updates_by_month = []
118
    uninstalls_by_month = []
119
    for m in range(start, end + 1):
120
        installs_by_month.append(MonthEvents(install_event_name, year, m))
121
        updates_by_month.append(MonthEvents(update_event_name, year, m))
122
        uninstalls_by_month.append(MonthEvents(uninstall_event_name, year, m))
123
    installs_data = [(datetime(year, start + i, 1).strftime("%Y-%m"), len(e)) for i, e in enumerate(installs_by_month)]
124
    updates_data = [(datetime(year, start + i, 1).strftime("%Y-%m"), len(e)) for i, e in enumerate(updates_by_month)]
125
    res = dict(new=installs_data, updates=updates_data)
126
    if platform != 'mac':
127
        uninstalls_data = [(datetime(year, start + i, 1).strftime("%Y-%m"), len(e)) for i, e in enumerate(uninstalls_by_month)]
128
        res.update(dict(uninstalls=uninstalls_data))
129
    return res
130
131
132
def get_users_statistics_weeks(app_id=None):
133
    now = timezone.now()
134
    event_name = 'request:%s' % app_id if app_id else 'request'
135
    year = now.year
136
    current_week = now.isocalendar()[1]
137
    previous_week = (now - timedelta(weeks=1)).isocalendar()[1]
138
    yesterday = now - timedelta(days=1)
139
    data = [
140
        ('Previous week', len(WeekEvents(event_name, year, previous_week))),
141
        ('Current week', len(WeekEvents(event_name, year, current_week))),
142
        ('Yesterday', len(DayEvents(event_name, year, yesterday.month, yesterday.day))),
143
        ('Today', len(DayEvents(event_name, year, now.month, now.day))),
144
    ]
145
    return data
146
147
148
def get_channel_statistics(app_id, date=None):
149
    if not date:
150
        date = timezone.now()
151
152
    event_name = 'request:{}:{}'
153
    channels = [c.name for c in Channel.objects.all()]
154
    data = [(channel, len(MonthEvents(event_name.format(app_id, channel), date.year, date.month))) for channel in channels]
155
    data = filter(lambda x: x[1], data)
156
    return data
157
158
159
def get_users_versions_by_platform(app_id, platform, date):
160
    if platform == 'win':
161
        versions = [str(v.version) for v in Version.objects.filter_by_enabled(app__id=app_id)]
162
    else:
163
        versions = [str(v.short_version) for v in SparkleVersion.objects.filter_by_enabled(app__id=app_id)]
164
    event_name = 'request:{}:{}:{}'
165
    data = [(v, len(MonthEvents(event_name.format(app_id, platform, v), date.year, date.month))) for v in versions]
166
    return data
167
168
169
def get_users_versions(app_id, date=None):
170
    if not date:
171
        date = timezone.now()
172
173
    win_data = get_users_versions_by_platform(app_id, 'win', date)
174
    win_data = filter(lambda x: x[1], win_data)
175
176
    mac_data = get_users_versions_by_platform(app_id, 'mac', date)
177
    mac_data = filter(lambda x: x[1], mac_data)
178
179
    data = dict(win=dict(win_data), mac=dict(mac_data))
180
181
    return data
182
183
184
185 View Code Duplication
def get_hourly_data_by_platform(app_id, end, n_hours, versions, platform, channel, tz='UTC'):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
186
    def build_event_name(app_id, platform, channel, v):
187
        if channel:
188
            return "request:{}:{}:{}:{}".format(app_id, platform, channel, v)
189
        else:
190
            return "request:{}:{}:{}".format(app_id, platform, v)
191
192
    tzinfo = pytz.timezone(tz)
193
    start = end - timezone.timedelta(hours=n_hours)
194
195
    hours = [datetime(start.year, start.month, start.day, start.hour, tzinfo=pytz.UTC)
196
             + timezone.timedelta(hours=x) for x in range(1, n_hours + 1)]
197
198
    data = [(v, [[hour.astimezone(tzinfo).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
199
                  len(HourEvents.from_date(build_event_name(app_id, platform, channel, v), hour))]
200
                 for hour in hours])
201
            for v in versions]
202
    data = filter(lambda version_data: sum([data[1] for data in version_data[1]]), data)
203
    return dict(data)
204
205
206 View Code Duplication
def get_daily_data_by_platform(app_id, end, n_days, versions, platform, channel):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
207
    def build_event_name(app_id, platform, channel, v):
208
        if channel:
209
            return "request:{}:{}:{}:{}".format(app_id, platform, channel, v)
210
        else:
211
            return "request:{}:{}:{}".format(app_id, platform, v)
212
213
    start = end - timezone.timedelta(days=n_days)
214
215
    days = [start + timezone.timedelta(days=x) for x in range(0, n_days+1)]
216
    data = [(v, [[day.strftime("%Y-%m-%dT00:%M:%S.%fZ"),
217
                  len(DayEvents.from_date(build_event_name(app_id, platform, channel, v), day))]
218
                 for day in days])
219
            for v in versions]
220
    data = filter(lambda version_data: sum([data[1] for data in version_data[1]]), data)
221
    return dict(data)
222
223
224
def get_users_live_versions(app_id, start, end, channel, tz='UTC'):
225
    import logging
226
    logging.info("Getting active versions from DB")
227
    win_versions = [str(v.version) for v in Version.objects.filter_by_enabled(app__id=app_id)]
228
    mac_versions = [str(v.short_version) for v in SparkleVersion.objects.filter_by_enabled(app__id=app_id)]
229
    logging.info("Getting statistics from Redis")
230
    if start < timezone.now() - timedelta(days=7):
231
        n_days = (end-start).days
232
        win_data = get_daily_data_by_platform(app_id, end, n_days, win_versions, 'win', channel)
233
        mac_data = get_daily_data_by_platform(app_id, end, n_days, mac_versions, 'mac', channel)
234
    else:
235
        tmp_hours = divmod((end - start).total_seconds(), 60 * 60)
236
        n_hours = tmp_hours[0] + 1
237
        n_hours = int(n_hours)
238
        win_data = get_hourly_data_by_platform(app_id, end, n_hours, win_versions, 'win', channel, tz=tz)
239
        mac_data = get_hourly_data_by_platform(app_id, end, n_hours, mac_versions, 'mac', channel, tz=tz)
240
241
    data = dict(win=win_data, mac=mac_data)
242
243
    return data
244
245
246
@valuedispatch
247
def is_user_active(period, userid):
248
    return False
249
250
251
@is_user_active.register(ACTIVE_USERS_DICT_CHOICES['all'])
252
def _(period, userid):
253
    return True
254
255
256
@is_user_active.register(ACTIVE_USERS_DICT_CHOICES['week'])
257
def _(period, userid):
258
    return get_id(userid) in WeekEvents.from_date('request', timezone.now())
259
260
261
@is_user_active.register(ACTIVE_USERS_DICT_CHOICES['month'])
262
def _(period, userid):
263
    return get_id(userid) in MonthEvents.from_date('request', timezone.now())
264
265
266
def get_kwargs_for_model(cls, obj, exclude=None):
267
    exclude = exclude or []
268
    fields = [(field.name, field.to_python) for field in cls._meta.fields if field.name not in exclude]
269
    kwargs = dict([(i, convert(obj.get(i))) for (i, convert) in fields])
270
    return kwargs
271
272
273
def parse_os(os):
274
    kwargs = get_kwargs_for_model(Os, os, exclude=['id'])
275
    obj, flag = Os.objects.get_or_create(**kwargs)
276
    return obj
277
278
279
def parse_hw(hw):
280
    kwargs = get_kwargs_for_model(Hw, hw, exclude=['id'])
281
    obj, flag = Hw.objects.get_or_create(**kwargs)
282
    return obj
283
284
285
def parse_req(request, ip=None):
286
    kwargs = get_kwargs_for_model(Request, request, exclude=['os', 'hw', 'created', 'id'])
287
    kwargs['ip'] = ip
288
    return Request(**kwargs)
289
290
291
def parse_apps(apps, request):
292
    app_list = []
293
    for app in apps:
294
        events = app.findall('event')
295
296
        if not events:
297
            continue
298
299
        kwargs = get_kwargs_for_model(AppRequest, app, exclude=['request', 'version', 'nextversion', 'id'])
300
        kwargs['version'] = app.get('version') or None
301
        kwargs['nextversion'] = app.get('nextversion') or None
302
        kwargs['tag'] = parser.get_channel(app)
303
        app_req = AppRequest.objects.create(request=request, **kwargs)
304
        event_list = parse_events(events)
305
        app_req.events.add(*event_list)
306
        app_list.append(app_req)
307
    return app_list
308
309
310
def parse_events(events):
311
    res = []
312
    for event in events:
313
        kwargs = get_kwargs_for_model(Event, event)
314
        res.append(Event.objects.create(**kwargs))
315
    return res
316
317
318
@transaction.atomic
319
def collect_statistics(request, ip=None):
320
    userid = request.get('userid')
321
    apps = request.findall('app')
322
323
    if userid:
324
        userid_counting(userid, apps, request.os.get('platform'))
325
326
    if not filter(lambda app: bool(app.findall('event')), apps):
327
        return
328
329
    req = parse_req(request, ip)
330
    req.os = parse_os(request.os)
331
    req.hw = parse_hw(request.hw) if request.get('hw') else None
332
    req.save()
333
334
    parse_apps(apps, req)
335