mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 21:43:21 +00:00
analytics: Add 15day_actives CountStat.
This commit is contained in:
@@ -375,6 +375,24 @@ count_message_by_stream_query = """
|
||||
"""
|
||||
zerver_count_message_by_stream = ZerverCountQuery(Message, StreamCount, count_message_by_stream_query)
|
||||
|
||||
check_useractivityinterval_by_user_query = """
|
||||
INSERT INTO analytics_usercount
|
||||
(user_id, realm_id, value, property, subgroup, end_time)
|
||||
SELECT
|
||||
zerver_userprofile.id, zerver_userprofile.realm_id, 1, '%(property)s', %(subgroup)s, %%(time_end)s
|
||||
FROM zerver_userprofile
|
||||
JOIN zerver_useractivityinterval
|
||||
ON
|
||||
zerver_userprofile.id = zerver_useractivityinterval.user_profile_id
|
||||
WHERE
|
||||
zerver_useractivityinterval.end >= %%(time_start)s AND
|
||||
zerver_useractivityinterval.start < %%(time_end)s
|
||||
%(join_args)s
|
||||
GROUP BY zerver_userprofile.id %(group_by_clause)s
|
||||
"""
|
||||
zerver_check_useractivityinterval_by_user = ZerverCountQuery(
|
||||
UserActivityInterval, UserCount, check_useractivityinterval_by_user_query)
|
||||
|
||||
def do_pull_minutes_active(stat, start_time, end_time):
|
||||
# type: (CountStat, datetime, datetime) -> None
|
||||
timer_start = time.time()
|
||||
@@ -410,6 +428,10 @@ count_stats_ = [
|
||||
(Message, 'sending_client_id'), CountStat.DAY),
|
||||
CountStat('messages_in_stream:is_bot:day', zerver_count_message_by_stream, {},
|
||||
(UserProfile, 'is_bot'), CountStat.DAY),
|
||||
# The minutes=15 part is due to the 15 minutes added in
|
||||
# zerver.lib.actions.do_update_user_activity_interval.
|
||||
CountStat('15day_actives::day', zerver_check_useractivityinterval_by_user, {},
|
||||
None, CountStat.DAY, interval=timedelta(days=15)-timedelta(minutes=15)),
|
||||
LoggingCountStat('active_users_log:is_bot:day', RealmCount, CountStat.DAY),
|
||||
CustomPullCountStat('minutes_active::day', UserCount, CountStat.DAY, do_pull_minutes_active)
|
||||
]
|
||||
|
||||
@@ -489,6 +489,50 @@ class TestCountStats(AnalyticsTestCase):
|
||||
user_profile=user, start=self.TIME_ZERO-start_offset,
|
||||
end=self.TIME_ZERO-end_offset)
|
||||
|
||||
def test_15day_actives(self):
|
||||
# type: () -> None
|
||||
stat = COUNT_STATS['15day_actives::day']
|
||||
self.current_property = stat.property
|
||||
|
||||
_15day = 15*self.DAY - 15*self.MINUTE
|
||||
|
||||
# Outside time range, should not appear. Also tests upper boundary.
|
||||
user1 = self.create_user()
|
||||
self.create_interval(user1, _15day + self.DAY, _15day + timedelta(seconds=1))
|
||||
self.create_interval(user1, timedelta(0), -self.HOUR)
|
||||
|
||||
# On lower boundary, should appear
|
||||
user2 = self.create_user()
|
||||
self.create_interval(user2, _15day + self.DAY, _15day)
|
||||
|
||||
# Multiple intervals, including one outside boundary
|
||||
user3 = self.create_user()
|
||||
self.create_interval(user3, 20*self.DAY, 19*self.DAY)
|
||||
self.create_interval(user3, 20*self.HOUR, 19*self.HOUR)
|
||||
self.create_interval(user3, 20*self.MINUTE, 19*self.MINUTE)
|
||||
|
||||
# Intervals crossing boundary
|
||||
user4 = self.create_user()
|
||||
self.create_interval(user4, 20*self.DAY, 10*self.DAY)
|
||||
user5 = self.create_user()
|
||||
self.create_interval(user5, self.MINUTE, -self.MINUTE)
|
||||
|
||||
# Interval subsuming time range
|
||||
user6 = self.create_user()
|
||||
self.create_interval(user6, 20*self.DAY, -2*self.DAY)
|
||||
|
||||
# Second realm
|
||||
user7 = self.create_user(realm=self.second_realm)
|
||||
self.create_interval(user7, 20*self.MINUTE, 19*self.MINUTE)
|
||||
|
||||
do_fill_count_stat_at_hour(stat, self.TIME_ZERO)
|
||||
self.assertTableState(UserCount, ['value', 'user'],
|
||||
[[1, user2], [1, user3], [1, user4], [1, user5], [1, user6], [1, user7]])
|
||||
self.assertTableState(RealmCount, ['value', 'realm'],
|
||||
[[5, self.default_realm], [1, self.second_realm]])
|
||||
self.assertTableState(InstallationCount, ['value'], [[6]])
|
||||
self.assertTableState(StreamCount, [], [])
|
||||
|
||||
def test_minutes_active(self):
|
||||
# type: () -> None
|
||||
stat = COUNT_STATS['minutes_active::day']
|
||||
|
||||
@@ -2301,6 +2301,8 @@ def streams_to_dicts_sorted(streams):
|
||||
|
||||
def do_update_user_activity_interval(user_profile, log_time):
|
||||
# type: (UserProfile, datetime.datetime) -> None
|
||||
# Update any stats in analytics.lib.counts.count_stats_ that rely on
|
||||
# this if the 15 minutes is changed to something else.
|
||||
effective_end = log_time + datetime.timedelta(minutes=15)
|
||||
# This code isn't perfect, because with various races we might end
|
||||
# up creating two overlapping intervals, but that shouldn't happen
|
||||
|
||||
Reference in New Issue
Block a user