From 95dc0894d8d84c8a2f1e15c461350774f0b24322 Mon Sep 17 00:00:00 2001 From: Steve Howell Date: Fri, 1 Nov 2013 09:44:29 -0400 Subject: [PATCH] Add /realm_activity report. This report will eventually replace the per-realm report that is now accessible through /activity. In order not to disrupt Waseem, I'm leaving the old reports around until we've polished the new ones. The old report does 24 different queries to get per-realm user data. The new approach gets all the data at once, and it slices and dices the data in Python to accomodate our slightly quirky data model. On localhost, this is a typical query: LOG: duration: 5.668 ms statement: SELECT "zerver_useractivity"."id", "zerver_useractivity"."user_profile_id", "zerver_useractivity"."client_id", "zerver_useractivity"."query", "zerver_useractivity"."count", "zerver_useractivity"."last_visit", "zerver_userprofile"."id", "zerver_userprofile"."email", "zerver_client"."id", "zerver_client"."name" FROM "zerver_useractivity" INNER JOIN "zerver_userprofile" ON ("zerver_useractivity"."user_profile_id" = "zerver_userprofile"."id") INNER JOIN "zerver_realm" ON ("zerver_userprofile"."realm_id" = "zerver_realm"."id") INNER JOIN "zerver_client" ON ("zerver_useractivity"."client_id" = "zerver_client"."id") WHERE "zerver_realm"."domain" = 'zulip.com' ORDER BY "zerver_userprofile"."email" ASC, "zerver_useractivity"."last_visit" DESC (imported from commit 0c71f4e32fe5a40f4496749dc29ad3463868d55e) --- zerver/views/__init__.py | 85 +++++++++++++++++++++++++++++++++++++++- zproject/urls.py | 1 + 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/zerver/views/__init__.py b/zerver/views/__init__.py index 4cb2e5a047..ed822b7d8d 100644 --- a/zerver/views/__init__.py +++ b/zerver/views/__init__.py @@ -7,7 +7,7 @@ from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect, HttpResponseForbidden from django.shortcuts import render_to_response, redirect from django.template import RequestContext, loader -from django.utils.timezone import now +from django.utils.timezone import now, utc from django.utils.html import mark_safe from django.utils.cache import patch_cache_control from django.core.exceptions import ValidationError @@ -2392,6 +2392,22 @@ def get_activity(request, realm=REQ(default=None)): context_instance=RequestContext(request) ) +def get_user_activity_records_for_realm(realm): + fields = [ + 'user_profile__email', + 'query', + 'client__name', + 'count', + 'last_visit', + ] + + records = UserActivity.objects.filter( + user_profile__realm__domain=realm + ) + records = records.order_by("user_profile__email", "-last_visit") + records = records.select_related('user_profile', 'client').only(*fields) + return records + def get_user_activity_records_for_email(email): fields = [ 'query', @@ -2504,6 +2520,73 @@ def user_activity_summary_table(user_summary): ) return content +def realm_user_summary_table(all_records): + user_records = {} + + def by_email(record): + return record.user_profile.email + + for email, records in itertools.groupby(all_records, by_email): + user_records[email] = get_user_activity_summary(list(records)) + + def get_last_visit(user_summary, k): + if k in user_summary: + return user_summary[k]['last_visit'] + else: + return None + + rows = [] + for email, user_summary in user_records.items(): + send_time = get_last_visit(user_summary, 'send') + pointer_time = get_last_visit(user_summary, 'pointer') + rows.append((email, send_time, pointer_time)) + + never = datetime.datetime(1970, 1, 1).replace(tzinfo=utc) + def by_send_time(row): + return row[1] or never + + rows = sorted(rows, key=by_send_time, reverse=True) + + cols = [ + 'email', + 'send_time', + 'pointer_time' + ] + + title = 'Summary' + + data = dict( + rows=rows, + cols=cols, + title=title + ) + + content = loader.render_to_string( + 'zerver/ad_hoc_query.html', + dict(data=data) + ) + + return content + +@zulip_internal +def get_realm_activity(request, realm): + all_records = get_user_activity_records_for_realm(realm) + all_records = list(all_records) + + data = [] + content = realm_user_summary_table(all_records) + + user_content = dict(content=content) + data += [('Summary', user_content)] + + realm = None + title = realm + return render_to_response( + 'zerver/activity.html', + dict(data=data, realm=realm, title=title), + context_instance=RequestContext(request) + ) + @zulip_internal def get_user_activity(request, email): records = get_user_activity_records_for_email(email) diff --git a/zproject/urls.py b/zproject/urls.py index 6e9e977168..f393338419 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -40,6 +40,7 @@ urlpatterns = patterns('', url(r'^activity$', 'zerver.views.get_activity'), + url(r'^realm_activity/(?P[\S]+)/$', 'zerver.views.get_realm_activity'), url(r'^user_activity/(?P[\S]+)/$', 'zerver.views.get_user_activity'), # Registration views, require a confirmation ID.