2013-04-30 23:58:59 +02:00
|
|
|
from __future__ import absolute_import
|
2015-11-01 17:11:06 +01:00
|
|
|
from __future__ import print_function
|
2013-04-30 23:58:59 +02:00
|
|
|
|
|
|
|
from django.core.management.base import BaseCommand
|
2017-02-25 21:50:51 +01:00
|
|
|
from django.utils import timezone
|
2017-03-03 19:01:52 +01:00
|
|
|
from typing import Any, Dict, List
|
2013-04-30 23:58:59 +02:00
|
|
|
|
2013-07-29 23:03:31 +02:00
|
|
|
from zerver.models import UserPresence, UserActivity
|
|
|
|
from zerver.lib.utils import statsd, statsd_key
|
2013-04-30 23:58:59 +02:00
|
|
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
help = """Sends active user statistics to statsd.
|
|
|
|
|
|
|
|
Run as a cron job that runs every 10 minutes."""
|
|
|
|
|
|
|
|
def handle(self, *args, **options):
|
2016-06-04 16:52:18 +02:00
|
|
|
# type: (*Any, **Any) -> None
|
2013-04-30 23:58:59 +02:00
|
|
|
# Get list of all active users in the last 1 week
|
2017-02-26 03:37:55 +01:00
|
|
|
cutoff = timezone.now() - timedelta(minutes=30, hours=168)
|
2013-04-30 23:58:59 +02:00
|
|
|
|
|
|
|
users = UserPresence.objects.select_related().filter(timestamp__gt=cutoff)
|
|
|
|
|
2013-05-03 16:22:28 +02:00
|
|
|
# Calculate 10min, 2hrs, 12hrs, 1day, 2 business days (TODO business days), 1 week bucket of stats
|
|
|
|
hour_buckets = [0.16, 2, 12, 24, 48, 168]
|
2016-01-26 02:44:57 +01:00
|
|
|
user_info = defaultdict(dict) # type: Dict[str, Dict[float, List[str]]]
|
2013-04-30 23:58:59 +02:00
|
|
|
|
|
|
|
for last_presence in users:
|
|
|
|
if last_presence.status == UserPresence.IDLE:
|
|
|
|
known_active = last_presence.timestamp - timedelta(minutes=30)
|
|
|
|
else:
|
|
|
|
known_active = last_presence.timestamp
|
|
|
|
|
|
|
|
for bucket in hour_buckets:
|
2017-01-08 19:48:22 +01:00
|
|
|
if bucket not in user_info[last_presence.user_profile.realm.string_id]:
|
|
|
|
user_info[last_presence.user_profile.realm.string_id][bucket] = []
|
2017-02-25 21:50:51 +01:00
|
|
|
if timezone.now() - known_active < timedelta(hours=bucket):
|
2017-01-08 19:48:22 +01:00
|
|
|
user_info[last_presence.user_profile.realm.string_id][bucket].append(last_presence.user_profile.email)
|
2013-04-30 23:58:59 +02:00
|
|
|
|
|
|
|
for realm, buckets in user_info.items():
|
2015-12-01 17:11:16 +01:00
|
|
|
print("Realm %s" % (realm,))
|
2013-04-30 23:58:59 +02:00
|
|
|
for hr, users in sorted(buckets.items()):
|
|
|
|
print("\tUsers for %s: %s" % (hr, len(users)))
|
2016-05-04 23:19:13 +02:00
|
|
|
statsd.gauge("users.active.%s.%shr" % (statsd_key(realm, True), statsd_key(hr, True)), len(users))
|
2013-04-30 23:58:59 +02:00
|
|
|
|
2013-05-13 19:18:50 +02:00
|
|
|
# Also do stats for how many users have been reading the app.
|
2016-04-01 12:16:09 +02:00
|
|
|
users_reading = UserActivity.objects.select_related().filter(query="/json/messages/flags")
|
2013-05-23 19:23:48 +02:00
|
|
|
user_info = defaultdict(dict)
|
2013-05-13 19:18:50 +02:00
|
|
|
for activity in users_reading:
|
|
|
|
for bucket in hour_buckets:
|
2017-01-08 19:48:22 +01:00
|
|
|
if bucket not in user_info[activity.user_profile.realm.string_id]:
|
|
|
|
user_info[activity.user_profile.realm.string_id][bucket] = []
|
2017-02-25 21:50:51 +01:00
|
|
|
if timezone.now() - activity.last_visit < timedelta(hours=bucket):
|
2017-01-08 19:48:22 +01:00
|
|
|
user_info[activity.user_profile.realm.string_id][bucket].append(activity.user_profile.email)
|
2013-05-13 19:18:50 +02:00
|
|
|
for realm, buckets in user_info.items():
|
2015-12-01 17:11:16 +01:00
|
|
|
print("Realm %s" % (realm,))
|
2013-05-13 19:18:50 +02:00
|
|
|
for hr, users in sorted(buckets.items()):
|
|
|
|
print("\tUsers reading for %s: %s" % (hr, len(users)))
|
2016-05-04 23:19:13 +02:00
|
|
|
statsd.gauge("users.reading.%s.%shr" % (statsd_key(realm, True), statsd_key(hr, True)), len(users))
|