diff --git a/zerver/lib/statistics.py b/zerver/lib/statistics.py index 57b5ba7d8e..e53b1654ba 100644 --- a/zerver/lib/statistics.py +++ b/zerver/lib/statistics.py @@ -5,13 +5,16 @@ from __future__ import division from zerver.models import UserProfile, UserActivity, UserActivityInterval, Message from django.utils.timezone import utc +from typing import Any + +from datetime import datetime, timedelta -from datetime import timedelta from itertools import chain from six.moves import range import six def median(data): + # type: (List[float]) -> float data = sorted(data) size = len(data) @@ -27,29 +30,35 @@ users_who_sent_query = Message.objects.select_related("sender") \ .exclude(sending_client__name__contains="API") def active_users(): + # type: () -> List[UserProfile] # Return a list of active users we want to count towards various # statistics. return UserProfile.objects.filter(is_bot=False, is_active=True).select_related() def users_who_sent_between(begin, end): + # type: (datetime, datetime) -> Set[int] sender_objs = users_who_sent_query.filter(pub_date__gt=begin, pub_date__lt=end) \ .values("sender__id") return set(s["sender__id"] for s in sender_objs) def users_who_sent_ever(): + # type: () -> Set[int] return set(s["sender__id"] for s in users_who_sent_query.values("sender__id")) def active_users_to_measure(): + # type: () -> List[UserProfile] senders = users_who_sent_ever() return [u for u in active_users() if u.id in senders] def active_users_who_sent_between(begin, end): + # type: (datetime, datetime) -> List[UserProfile] senders = users_who_sent_between(begin, end) return [u for u in active_users() if u.id in senders] # Return the amount of Zulip usage for this user between the two # given dates def seconds_usage_between(user_profile, begin, end): + # type: (UserProfile, datetime, datetime) -> timedelta intervals = UserActivityInterval.objects.filter(user_profile=user_profile, end__gte=begin, start__lte=end) duration = timedelta(0) for interval in intervals: @@ -60,6 +69,7 @@ def seconds_usage_between(user_profile, begin, end): # Return a list of how many seconds each user has been engaging with the app on a given day def seconds_active_during_day(day): + # type: (datetime) -> List[float] begin_day = day.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=utc) end_day = day.replace(hour=23, minute=59, second=59, microsecond=0, tzinfo=utc) active_users = active_users_to_measure() @@ -67,6 +77,7 @@ def seconds_active_during_day(day): return [seconds_usage_between(user, begin_day, end_day).total_seconds() for user in active_users] def users_active_nosend_during_day(day): + # type: (datetime) -> List[UserProfile] begin_day = day.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=utc) end_day = day.replace(hour=23, minute=59, second=59, microsecond=0, tzinfo=utc) active_users = active_users_to_measure() @@ -82,6 +93,7 @@ def users_active_nosend_during_day(day): return [u for u in today_users if not u.id in today_senders] def calculate_stats(data, all_users): + # type: (List[float], List[UserProfile]) -> Dict[str, Any] if len(data) == 0: return {"# data points": 0} @@ -97,6 +109,7 @@ def calculate_stats(data, all_users): # Return an info dict {mean: , median} containing the mean/median seconds users were active on a given day def activity_averages_during_day(day): + # type: (datetime) -> Dict[str, Any] users_to_measure = active_users_to_measure() seconds_active = seconds_active_during_day(day) return calculate_stats(seconds_active, all_users=users_to_measure) @@ -104,6 +117,7 @@ def activity_averages_during_day(day): # Returns an info dict {mean: , median} with engagement numbers for all users according # to active_users_to_measure. def activity_averages_between(begin, end, by_day=True): + # type: (datetime, datetime, bool) -> Dict[str, Any] seconds_active = {} users_to_measure = active_users_to_measure() for i in range((end - begin).days):