from datetime import timedelta datetime from sets import Set ONLINE_MI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from datetime import timedelta, datetime
from sets import Set
ONLINE_MINUTES = 2
PURGE_MULTIPLE = 20
_users_seen_per_minute = {}
_last_purged = datetime.now()
def get_online_user_ids():
"""
Returns a sets.Set of user_id's which have made requests in the last ONLINE_MINUTES.
Includes partial minutes (i.e. 2 minutes would count up to users seen in 2:59, due to minute rollover).
"""
current_minute = _get_minute()
user_set = Set()
for count_minute in [_get_minute(current_minute, -1 * i) for i in range(0, ONLINE_MINUTES+1)]:
try:
user_set.union_update(_users_seen_per_minute[count_minute])
except KeyError:
pass #perhaps no requests that minute?
return user_set
def _get_minute(base_datetime=None, offset_minutes=0):
if base_datetime is None:
base_datetime = datetime.now()
if offset_minutes != 0:
offset = timedelta(minutes=offset_minutes)
time = base_datetime + offset
else:
time = base_datetime
return datetime(time.year, time.month, time.day, time.hour, time.minute)
def _purge_old_minutes():
global _last_purged
now = datetime.now()
purge_at = _last_purged + timedelta(minutes=PURGE_MULTIPLE*ONLINE_MINUTES)
if purge_at < now:
purge_older_than = now - timedelta(minutes=ONLINE_MINUTES+1)
for seen_minute in _users_seen_per_minute.keys():
if seen_minute < purge_older_than:
del _users_seen_per_minute[seen_minute]
_last_purged = now
else:
pass #not time to purge yet
class OnlineUsers(object):
def process_request(self, request): #assumes auth middleware earlier in the request chain
assert hasattr(request, 'user'), "The OnlineUsers middleware requires auth middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.auth.middleware.AuthenticationMiddleware'."
if request.user.is_anonymous(): #assuming most users are anonymous, this is a fast path for them
return #but move down -after- purge if you expect << 1 non-anonymous request per minute.
_purge_old_minutes()
current_minute = _get_minute()
try:
_users_seen_per_minute[current_minute].add(request.user.id)
except KeyError: #request is first in new minute
_users_seen_per_minute[current_minute] = Set([request.user.id])