From 302cfcd48c00cc8725d77b3605a3681db22a234c Mon Sep 17 00:00:00 2001 From: Leo Franchi Date: Thu, 4 Apr 2013 18:13:03 -0400 Subject: [PATCH] Send client information for initial presence and process time differential (imported from commit 99a51b7cc8b6c51c4e82757a984d07603b2980e3) --- zephyr/lib/actions.py | 34 ++++++++++++++++++++--------- zephyr/lib/cache.py | 7 ++++++ zephyr/models.py | 10 +++++++-- zephyr/static/js/activity.js | 42 ++++++++++++++++++++---------------- zephyr/static/js/zephyr.js | 4 ++-- zephyr/views.py | 18 +++------------- 6 files changed, 68 insertions(+), 47 deletions(-) diff --git a/zephyr/lib/actions.py b/zephyr/lib/actions.py index a9d1cf5f0e..03b87a12d4 100644 --- a/zephyr/lib/actions.py +++ b/zephyr/lib/actions.py @@ -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'] diff --git a/zephyr/lib/cache.py b/zephyr/lib/cache.py index 6d3546a996..0382b4a815 100644 --- a/zephyr/lib/cache.py +++ b/zephyr/lib/cache.py @@ -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)) diff --git a/zephyr/models.py b/zephyr/models.py index c42fbcd1c2..c273f6de39 100644 --- a/zephyr/models.py +++ b/zephyr/models.py @@ -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) diff --git a/zephyr/static/js/activity.js b/zephyr/static/js/activity.js index b5f01e9f52..e7edcd0357 100644 --- a/zephyr/static/js/activity.js +++ b/zephyr/static/js/activity.js @@ -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(); }; diff --git a/zephyr/static/js/zephyr.js b/zephyr/static/js/zephyr.js index f6e6368819..02c64479ea 100644 --- a/zephyr/static/js/zephyr.js +++ b/zephyr/static/js/zephyr.js @@ -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; } }); diff --git a/zephyr/views.py b/zephyr/views.py index 102d77abc9..aa3b0f56c9 100644 --- a/zephyr/views.py +++ b/zephyr/views.py @@ -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