Send client information for initial presence and process time differential

(imported from commit 99a51b7cc8b6c51c4e82757a984d07603b2980e3)
This commit is contained in:
Leo Franchi
2013-04-04 18:13:03 -04:00
parent 5d4b2305fe
commit 302cfcd48c
6 changed files with 68 additions and 47 deletions

View File

@@ -23,7 +23,7 @@ from django.utils import timezone
from zephyr.lib.create_user import create_user
from zephyr.lib import bugdown
from zephyr.lib.cache import cache_with_key, user_profile_by_id_cache_key, \
user_profile_by_email_cache_key
user_profile_by_email_cache_key, status_dict_cache_key
from zephyr.decorator import get_user_profile_by_email, json_to_list, JsonableError
from zephyr.lib.event_queue import request_event_queue, get_user_events
@@ -38,6 +38,7 @@ import datetime
import os
import platform
import logging
from collections import defaultdict
from os import path
# Store an event in the log for re-importing messages
@@ -581,8 +582,9 @@ def process_user_activity_event(event):
return do_update_user_activity(user_profile, client, query, log_time)
def send_presence_changed(user_profile, presence):
presence_dict = presence.to_dict()
notice = dict(event=dict(type="presence", email=user_profile.email,
presence=presence.to_dict()),
presence={presence_dict['client']: presence.to_dict()}),
users=[up.id for up in
UserProfile.objects.select_related()
.filter(realm=user_profile.realm,
@@ -602,8 +604,9 @@ def do_update_user_presence(user_profile, client, log_time, status):
client = client)
created = False
became_online = (status == UserPresence.ACTIVE and
presence.status == UserPresence.IDLE)
stale_status = (log_time - presence.timestamp) > datetime.timedelta(minutes=10)
was_idle = presence.status == UserPresence.IDLE
became_online = (status == UserPresence.ACTIVE) and (stale_status or was_idle)
presence.timestamp = log_time
presence.status = status
@@ -765,6 +768,21 @@ def gather_subscriptions(user_profile):
return sorted(result)
@cache_with_key(status_dict_cache_key, timeout=60)
def get_status_dict(requesting_user_profile):
user_statuses = defaultdict(dict)
# Return no status info for MIT
if requesting_user_profile.realm.domain == 'mit.edu':
return user_statuses
for presence in UserPresence.objects.filter(user_profile__realm=requesting_user_profile.realm) \
.select_related('user_profile', 'client'):
user_statuses[presence.user_profile.email][presence.client.name] = presence.to_dict()
return user_statuses
def do_events_register(user_profile, apply_markdown=True, event_types=None):
queue_id = request_event_queue(user_profile, apply_markdown, event_types)
if queue_id is None:
@@ -796,11 +814,7 @@ def do_events_register(user_profile, apply_markdown=True, event_types=None):
if event_types is None or "subscription" in event_types:
ret['subscriptions'] = gather_subscriptions(user_profile)
if event_types is None or "presence" in event_types:
presences = dict((presence.user_profile.email, presence.to_dict())
for presence in UserPresence.objects.select_related()
.filter(user_profile__realm=user_profile.realm,
user_profile__is_active=True))
ret['presences'] = presences
ret['presences'] = get_status_dict(user_profile)
# Apply events that came in while we were fetching initial data
events = get_user_events(user_profile, queue_id, -1)
@@ -824,7 +838,7 @@ def do_events_register(user_profile, apply_markdown=True, event_types=None):
ret['subscriptions'] = filter(lambda s: s['name'] != sub['name'],
ret['subscriptions'])
elif event['type'] == "presence":
ret['presences'][event['email']] = event['presence']
ret['presences'][event['email']][event['presence']['client']] = event['presence']
if events:
ret['last_event_id'] = events[-1]['id']

View File

@@ -73,3 +73,10 @@ def update_user_profile_cache(sender, **kwargs):
items_for_memcached[user_profile_by_email_cache_key(user_profile.email)] = (user_profile,)
items_for_memcached[user_profile_by_id_cache_key(user_profile.id)] = (user_profile,)
djcache.set_many(items_for_memcached)
def status_dict_cache_key(user_profile):
return "status_dict:%d" % (user_profile.realm_id,)
def update_user_presence_cache(sender, **kwargs):
user_profile = kwargs['instance'].user_profile
djcache.delete(status_dict_cache_key(user_profile))

View File

@@ -2,7 +2,8 @@ from django.db import models
from django.conf import settings
from django.contrib.auth.models import AbstractBaseUser, UserManager
from zephyr.lib.cache import cache_with_key, update_user_profile_cache, \
user_profile_by_id_cache_key, user_profile_by_email_cache_key
user_profile_by_id_cache_key, user_profile_by_email_cache_key, \
update_user_presence_cache
from zephyr.lib.utils import make_safe_digest
import os
from django.db import transaction, IntegrityError
@@ -444,7 +445,8 @@ class UserPresence(models.Model):
elif self.status == UserPresence.IDLE:
presence_val = 'idle'
return {'status' : presence_val,
return {'client' : self.client.name,
'status' : presence_val,
'timestamp': datetime_to_timestamp(self.timestamp)}
@staticmethod
@@ -461,6 +463,10 @@ class UserPresence(models.Model):
class Meta:
unique_together = ("user_profile", "client")
# Flush the cached user status_dict whenever a user's presence
# changes
post_save.connect(update_user_presence_cache, sender=UserPresence)
class DefaultStream(models.Model):
realm = models.ForeignKey(Realm)
stream = models.ForeignKey(Stream)

View File

@@ -72,6 +72,24 @@ function update_users() {
ui.set_presence_list(users, user_info);
}
function status_from_timestamp(time_now, presence) {
if (presence.website === undefined) {
return 'idle';
}
var age = time_now - presence.website.timestamp;
var status = 'idle';
if (presence.website.status === ACTIVE && age >= 0) {
if (age < AWAY_THRESHOLD_SECS) {
status = 'active';
} else if (age < IDLE_THRESHOLD_SECS) {
status = 'away';
}
}
return status;
}
function focus_ping() {
if (!has_focus) {
return;
@@ -90,23 +108,8 @@ function focus_ping() {
// Ping returns the active peer list
$.each(data.presences, function (this_email, presence) {
var age = -1;
if (presence.website !== undefined && presence.website.timestamp !== undefined) {
age = now - presence.website.timestamp;
}
if (page_params.email !== this_email) {
var status = 'idle';
if (presence.website !== undefined
&& presence.website.status === ACTIVE && age >= 0) {
if (age < AWAY_THRESHOLD_SECS) {
status = 'active';
} else if (age < IDLE_THRESHOLD_SECS) {
status = 'away';
}
}
user_info[this_email] = status;
user_info[this_email] = status_from_timestamp(now, presence);
}
});
update_users();
@@ -130,14 +133,17 @@ exports.initialize = function () {
keepTracking: true});
ping_timer = setInterval(focus_ping, ACTIVE_PING_INTERVAL_MS);
focus_ping();
};
exports.set_user_status = function (user_email, status) {
exports.set_user_status = function (user_email, presence) {
if (user_email === page_params.email) {
return;
}
var now = new Date().getTime() / 1000;
user_info[user_email] = status_from_timestamp(now, presence);
user_info[user_email] = status;
update_users();
};

View File

@@ -61,7 +61,7 @@ $(function () {
"full_name": "Humbug Feedback Bot"}]);
$.each(page_params.initial_presences, function (email, presence) {
activity.set_user_status(email, presence.status);
activity.set_user_status(email, presence);
});
});
@@ -694,7 +694,7 @@ function get_updates(options) {
}
break;
case 'presence':
activity.set_user_status(event.email, event.presence.status);
activity.set_user_status(event.email, event.presence);
break;
}
});

View File

@@ -26,7 +26,8 @@ from zephyr.lib.actions import do_add_subscription, do_remove_subscription, \
log_subscription_property_change, internal_send_message, \
create_stream_if_needed, gather_subscriptions, subscribed_to_stream, \
update_user_presence, set_stream_color, get_stream_colors, update_message_flags, \
recipient_for_emails, extract_recipients, do_events_register, do_finish_tutorial
recipient_for_emails, extract_recipients, do_events_register, do_finish_tutorial, \
get_status_dict
from zephyr.forms import RegistrationForm, HomepageForm, ToSForm, is_unique, \
is_inactive, isnt_mit
from django.views.decorators.csrf import csrf_exempt
@@ -1459,21 +1460,8 @@ def api_beanstalk_webhook(request, user_profile, payload=POST(converter=json_to_
return json_error(ret)
return json_success()
@cache_with_key(lambda user_profile: user_profile.realm_id, timeout=60)
def get_status_list(requesting_user_profile):
user_statuses = defaultdict(dict)
# Return no status info for MIT
if requesting_user_profile.realm.domain == 'mit.edu':
return {'presences': user_statuses}
for presence in UserPresence.objects.filter(
user_profile__realm=requesting_user_profile.realm).select_related(
'user_profile', 'client'):
user_statuses[presence.user_profile.email][presence.client.name] = presence.to_dict()
return {'presences': user_statuses}
return {'presences': get_status_dict(requesting_user_profile)}
@authenticated_json_post_view
@has_request_variables