2021-06-17 23:15:42 +02:00
from datetime import datetime , timedelta , timezone
2024-07-12 02:30:17 +02:00
from typing import Optional
2021-06-17 23:15:42 +02:00
from django . utils . timezone import now as timezone_now
2023-10-12 19:43:45 +02:00
from typing_extensions import override
2021-06-17 23:15:42 +02:00
from analytics . lib . counts import COUNT_STATS , CountStat
from analytics . lib . time_utils import time_range
2023-02-28 21:04:11 +01:00
from analytics . models import FillState , RealmCount , StreamCount , UserCount
2021-06-17 23:25:14 +02:00
from analytics . views . stats import rewrite_client_arrays , sort_by_totals , sort_client_labels
2021-06-17 23:15:42 +02:00
from zerver . lib . test_classes import ZulipTestCase
from zerver . lib . timestamp import ceiling_to_day , ceiling_to_hour , datetime_to_timestamp
2023-12-15 02:14:24 +01:00
from zerver . models import Client
from zerver . models . realms import get_realm
2021-06-17 23:15:42 +02:00
class TestStatsEndpoint ( ZulipTestCase ) :
def test_stats ( self ) - > None :
self . user = self . example_user ( " hamlet " )
self . login_user ( self . user )
result = self . client_get ( " /stats " )
self . assertEqual ( result . status_code , 200 )
# Check that we get something back
self . assert_in_response ( " Zulip analytics for " , result )
def test_guest_user_cant_access_stats ( self ) - > None :
self . user = self . example_user ( " polonius " )
self . login_user ( self . user )
result = self . client_get ( " /stats " )
self . assert_json_error ( result , " Not allowed for guest users " , 400 )
result = self . client_get ( " /json/analytics/chart_data " )
self . assert_json_error ( result , " Not allowed for guest users " , 400 )
def test_stats_for_realm ( self ) - > None :
user = self . example_user ( " hamlet " )
self . login_user ( user )
result = self . client_get ( " /stats/realm/zulip/ " )
self . assertEqual ( result . status_code , 302 )
result = self . client_get ( " /stats/realm/not_existing_realm/ " )
self . assertEqual ( result . status_code , 302 )
user = self . example_user ( " hamlet " )
user . is_staff = True
user . save ( update_fields = [ " is_staff " ] )
result = self . client_get ( " /stats/realm/not_existing_realm/ " )
self . assertEqual ( result . status_code , 404 )
result = self . client_get ( " /stats/realm/zulip/ " )
self . assertEqual ( result . status_code , 200 )
self . assert_in_response ( " Zulip analytics for " , result )
def test_stats_for_installation ( self ) - > None :
user = self . example_user ( " hamlet " )
self . login_user ( user )
result = self . client_get ( " /stats/installation " )
self . assertEqual ( result . status_code , 302 )
user = self . example_user ( " hamlet " )
user . is_staff = True
user . save ( update_fields = [ " is_staff " ] )
result = self . client_get ( " /stats/installation " )
self . assertEqual ( result . status_code , 200 )
self . assert_in_response ( " Zulip analytics for " , result )
class TestGetChartData ( ZulipTestCase ) :
2023-10-12 19:43:45 +02:00
@override
2021-06-17 23:15:42 +02:00
def setUp ( self ) - > None :
super ( ) . setUp ( )
self . realm = get_realm ( " zulip " )
self . user = self . example_user ( " hamlet " )
2023-02-28 21:04:11 +01:00
self . stream_id = self . get_stream_id ( self . get_streams ( self . user ) [ 0 ] )
2021-06-17 23:15:42 +02:00
self . login_user ( self . user )
self . end_times_hour = [
ceiling_to_hour ( self . realm . date_created ) + timedelta ( hours = i ) for i in range ( 4 )
]
self . end_times_day = [
ceiling_to_day ( self . realm . date_created ) + timedelta ( days = i ) for i in range ( 4 )
]
2024-07-12 02:30:17 +02:00
def data ( self , i : int ) - > list [ int ] :
2021-06-17 23:15:42 +02:00
return [ 0 , 0 , i , 0 ]
def insert_data (
2024-07-12 02:30:17 +02:00
self , stat : CountStat , realm_subgroups : list [ Optional [ str ] ] , user_subgroups : list [ str ]
2021-06-17 23:15:42 +02:00
) - > None :
if stat . frequency == CountStat . HOUR :
insert_time = self . end_times_hour [ 2 ]
fill_time = self . end_times_hour [ - 1 ]
if stat . frequency == CountStat . DAY :
insert_time = self . end_times_day [ 2 ]
fill_time = self . end_times_day [ - 1 ]
RealmCount . objects . bulk_create (
RealmCount (
property = stat . property ,
subgroup = subgroup ,
end_time = insert_time ,
value = 100 + i ,
realm = self . realm ,
)
for i , subgroup in enumerate ( realm_subgroups )
)
UserCount . objects . bulk_create (
UserCount (
property = stat . property ,
subgroup = subgroup ,
end_time = insert_time ,
value = 200 + i ,
realm = self . realm ,
user = self . user ,
)
for i , subgroup in enumerate ( user_subgroups )
)
2023-02-28 21:04:11 +01:00
StreamCount . objects . bulk_create (
StreamCount (
property = stat . property ,
subgroup = subgroup ,
end_time = insert_time ,
value = 100 + i ,
stream_id = self . stream_id ,
realm = self . realm ,
)
for i , subgroup in enumerate ( realm_subgroups )
)
2021-06-17 23:15:42 +02:00
FillState . objects . create ( property = stat . property , end_time = fill_time , state = FillState . DONE )
def test_number_of_humans ( self ) - > None :
stat = COUNT_STATS [ " realm_active_humans::day " ]
self . insert_data ( stat , [ None ] , [ ] )
stat = COUNT_STATS [ " 1day_actives::day " ]
self . insert_data ( stat , [ None ] , [ ] )
stat = COUNT_STATS [ " active_users_audit:is_bot:day " ]
self . insert_data ( stat , [ " false " ] , [ ] )
result = self . client_get ( " /json/analytics/chart_data " , { " chart_name " : " number_of_humans " } )
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data ,
{
" msg " : " " ,
" end_times " : [ datetime_to_timestamp ( dt ) for dt in self . end_times_day ] ,
" frequency " : CountStat . DAY ,
" everyone " : {
" _1day " : self . data ( 100 ) ,
" _15day " : self . data ( 100 ) ,
" all_time " : self . data ( 100 ) ,
} ,
" display_order " : None ,
" result " : " success " ,
} ,
)
def test_messages_sent_over_time ( self ) - > None :
stat = COUNT_STATS [ " messages_sent:is_bot:hour " ]
self . insert_data ( stat , [ " true " , " false " ] , [ " false " ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data ,
{
" msg " : " " ,
" end_times " : [ datetime_to_timestamp ( dt ) for dt in self . end_times_hour ] ,
" frequency " : CountStat . HOUR ,
" everyone " : { " bot " : self . data ( 100 ) , " human " : self . data ( 101 ) } ,
" user " : { " bot " : self . data ( 0 ) , " human " : self . data ( 200 ) } ,
" display_order " : None ,
" result " : " success " ,
} ,
)
def test_messages_sent_by_message_type ( self ) - > None :
stat = COUNT_STATS [ " messages_sent:message_type:day " ]
self . insert_data (
stat , [ " public_stream " , " private_message " ] , [ " public_stream " , " private_stream " ]
)
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_by_message_type " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data ,
{
" msg " : " " ,
" end_times " : [ datetime_to_timestamp ( dt ) for dt in self . end_times_day ] ,
" frequency " : CountStat . DAY ,
" everyone " : {
2024-04-16 13:33:22 +02:00
" Public channels " : self . data ( 100 ) ,
" Private channels " : self . data ( 0 ) ,
2023-01-24 15:36:03 +01:00
" Direct messages " : self . data ( 101 ) ,
" Group direct messages " : self . data ( 0 ) ,
2021-06-17 23:15:42 +02:00
} ,
" user " : {
2024-04-16 13:33:22 +02:00
" Public channels " : self . data ( 200 ) ,
" Private channels " : self . data ( 201 ) ,
2023-01-24 15:36:03 +01:00
" Direct messages " : self . data ( 0 ) ,
" Group direct messages " : self . data ( 0 ) ,
2021-06-17 23:15:42 +02:00
} ,
" display_order " : [
2023-01-24 15:36:03 +01:00
" Direct messages " ,
2024-04-16 13:33:22 +02:00
" Public channels " ,
" Private channels " ,
2023-01-24 15:36:03 +01:00
" Group direct messages " ,
2021-06-17 23:15:42 +02:00
] ,
" result " : " success " ,
} ,
)
def test_messages_sent_by_client ( self ) - > None :
stat = COUNT_STATS [ " messages_sent:client:day " ]
client1 = Client . objects . create ( name = " client 1 " )
client2 = Client . objects . create ( name = " client 2 " )
client3 = Client . objects . create ( name = " client 3 " )
client4 = Client . objects . create ( name = " client 4 " )
2021-07-26 16:46:53 +02:00
self . insert_data (
stat ,
[ str ( client4 . id ) , str ( client3 . id ) , str ( client2 . id ) ] ,
[ str ( client3 . id ) , str ( client1 . id ) ] ,
)
2021-06-17 23:15:42 +02:00
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_by_client " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data ,
{
" msg " : " " ,
" end_times " : [ datetime_to_timestamp ( dt ) for dt in self . end_times_day ] ,
" frequency " : CountStat . DAY ,
" everyone " : {
" client 4 " : self . data ( 100 ) ,
" client 3 " : self . data ( 101 ) ,
" client 2 " : self . data ( 102 ) ,
} ,
" user " : { " client 3 " : self . data ( 200 ) , " client 1 " : self . data ( 201 ) } ,
" display_order " : [ " client 1 " , " client 2 " , " client 3 " , " client 4 " ] ,
" result " : " success " ,
} ,
)
def test_messages_read_over_time ( self ) - > None :
stat = COUNT_STATS [ " messages_read::hour " ]
self . insert_data ( stat , [ None ] , [ ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_read_over_time " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data ,
{
" msg " : " " ,
" end_times " : [ datetime_to_timestamp ( dt ) for dt in self . end_times_hour ] ,
" frequency " : CountStat . HOUR ,
" everyone " : { " read " : self . data ( 100 ) } ,
" user " : { " read " : self . data ( 0 ) } ,
" display_order " : None ,
" result " : " success " ,
} ,
)
2023-02-28 21:04:11 +01:00
def test_messages_sent_by_stream ( self ) - > None :
stat = COUNT_STATS [ " messages_in_stream:is_bot:day " ]
self . insert_data ( stat , [ " true " , " false " ] , [ ] )
result = self . client_get (
f " /json/analytics/chart_data/stream/ { self . stream_id } " ,
{
" chart_name " : " messages_sent_by_stream " ,
} ,
)
data = self . assert_json_success ( result )
self . assertEqual (
data ,
{
" msg " : " " ,
" end_times " : [ datetime_to_timestamp ( dt ) for dt in self . end_times_day ] ,
" frequency " : CountStat . DAY ,
" everyone " : { " bot " : self . data ( 100 ) , " human " : self . data ( 101 ) } ,
" display_order " : None ,
" result " : " success " ,
} ,
)
result = self . api_get (
self . example_user ( " polonius " ) ,
f " /api/v1/analytics/chart_data/stream/ { self . stream_id } " ,
{
" chart_name " : " messages_sent_by_stream " ,
} ,
)
self . assert_json_error ( result , " Not allowed for guest users " )
# Verify we correctly forbid access to stats of streams in other realms.
result = self . api_get (
self . mit_user ( " sipbtest " ) ,
f " /api/v1/analytics/chart_data/stream/ { self . stream_id } " ,
{
" chart_name " : " messages_sent_by_stream " ,
} ,
subdomain = " zephyr " ,
)
2024-04-16 20:31:49 +02:00
self . assert_json_error ( result , " Invalid channel ID " )
2023-02-28 21:04:11 +01:00
2021-06-17 23:15:42 +02:00
def test_include_empty_subgroups ( self ) - > None :
FillState . objects . create (
property = " realm_active_humans::day " ,
end_time = self . end_times_day [ 0 ] ,
state = FillState . DONE ,
)
result = self . client_get ( " /json/analytics/chart_data " , { " chart_name " : " number_of_humans " } )
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual ( data [ " everyone " ] , { " _1day " : [ 0 ] , " _15day " : [ 0 ] , " all_time " : [ 0 ] } )
self . assertFalse ( " user " in data )
FillState . objects . create (
property = " messages_sent:is_bot:hour " ,
end_time = self . end_times_hour [ 0 ] ,
state = FillState . DONE ,
)
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual ( data [ " everyone " ] , { " human " : [ 0 ] , " bot " : [ 0 ] } )
self . assertEqual ( data [ " user " ] , { " human " : [ 0 ] , " bot " : [ 0 ] } )
FillState . objects . create (
property = " messages_sent:message_type:day " ,
end_time = self . end_times_day [ 0 ] ,
state = FillState . DONE ,
)
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_by_message_type " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data [ " everyone " ] ,
{
2024-04-16 13:33:22 +02:00
" Public channels " : [ 0 ] ,
" Private channels " : [ 0 ] ,
2023-01-24 15:36:03 +01:00
" Direct messages " : [ 0 ] ,
" Group direct messages " : [ 0 ] ,
2021-06-17 23:15:42 +02:00
} ,
)
self . assertEqual (
data [ " user " ] ,
{
2024-04-16 13:33:22 +02:00
" Public channels " : [ 0 ] ,
" Private channels " : [ 0 ] ,
2023-01-24 15:36:03 +01:00
" Direct messages " : [ 0 ] ,
" Group direct messages " : [ 0 ] ,
2021-06-17 23:15:42 +02:00
} ,
)
FillState . objects . create (
property = " messages_sent:client:day " ,
end_time = self . end_times_day [ 0 ] ,
state = FillState . DONE ,
)
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_by_client " }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual ( data [ " everyone " ] , { } )
self . assertEqual ( data [ " user " ] , { } )
def test_start_and_end ( self ) - > None :
stat = COUNT_STATS [ " realm_active_humans::day " ]
self . insert_data ( stat , [ None ] , [ ] )
stat = COUNT_STATS [ " 1day_actives::day " ]
self . insert_data ( stat , [ None ] , [ ] )
stat = COUNT_STATS [ " active_users_audit:is_bot:day " ]
self . insert_data ( stat , [ " false " ] , [ ] )
end_time_timestamps = [ datetime_to_timestamp ( dt ) for dt in self . end_times_day ]
# valid start and end
result = self . client_get (
" /json/analytics/chart_data " ,
{
" chart_name " : " number_of_humans " ,
" start " : end_time_timestamps [ 1 ] ,
" end " : end_time_timestamps [ 2 ] ,
} ,
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual ( data [ " end_times " ] , end_time_timestamps [ 1 : 3 ] )
self . assertEqual (
data [ " everyone " ] , { " _1day " : [ 0 , 100 ] , " _15day " : [ 0 , 100 ] , " all_time " : [ 0 , 100 ] }
)
# start later then end
result = self . client_get (
" /json/analytics/chart_data " ,
{
" chart_name " : " number_of_humans " ,
" start " : end_time_timestamps [ 2 ] ,
" end " : end_time_timestamps [ 1 ] ,
} ,
)
self . assert_json_error_contains ( result , " Start time is later than " )
def test_min_length ( self ) - > None :
stat = COUNT_STATS [ " realm_active_humans::day " ]
self . insert_data ( stat , [ None ] , [ ] )
stat = COUNT_STATS [ " 1day_actives::day " ]
self . insert_data ( stat , [ None ] , [ ] )
stat = COUNT_STATS [ " active_users_audit:is_bot:day " ]
self . insert_data ( stat , [ " false " ] , [ ] )
# test min_length is too short to change anything
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " number_of_humans " , " min_length " : 2 }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
self . assertEqual (
data [ " end_times " ] , [ datetime_to_timestamp ( dt ) for dt in self . end_times_day ]
)
self . assertEqual (
data [ " everyone " ] ,
{ " _1day " : self . data ( 100 ) , " _15day " : self . data ( 100 ) , " all_time " : self . data ( 100 ) } ,
)
# test min_length larger than filled data
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " number_of_humans " , " min_length " : 5 }
)
2022-06-07 01:37:01 +02:00
data = self . assert_json_success ( result )
2021-06-17 23:15:42 +02:00
end_times = [
ceiling_to_day ( self . realm . date_created ) + timedelta ( days = i ) for i in range ( - 1 , 4 )
]
self . assertEqual ( data [ " end_times " ] , [ datetime_to_timestamp ( dt ) for dt in end_times ] )
self . assertEqual (
data [ " everyone " ] ,
{
" _1day " : [ 0 , * self . data ( 100 ) ] ,
" _15day " : [ 0 , * self . data ( 100 ) ] ,
" all_time " : [ 0 , * self . data ( 100 ) ] ,
} ,
)
def test_non_existent_chart ( self ) - > None :
result = self . client_get ( " /json/analytics/chart_data " , { " chart_name " : " does_not_exist " } )
self . assert_json_error_contains ( result , " Unknown chart name " )
def test_analytics_not_running ( self ) - > None :
realm = get_realm ( " zulip " )
self . assertEqual ( FillState . objects . count ( ) , 0 )
realm . date_created = timezone_now ( ) - timedelta ( days = 3 )
realm . save ( update_fields = [ " date_created " ] )
with self . assertLogs ( level = " WARNING " ) as m :
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assertEqual (
m . output ,
[
f " WARNING:root:User from realm zulip attempted to access /stats, but the computed start time: { realm . date_created } (creation of realm or installation) is later than the computed end time: 0001-01-01 00:00:00+00:00 (last successful analytics update). Is the analytics cron job running? "
] ,
)
self . assert_json_error_contains ( result , " No analytics data available " )
realm . date_created = timezone_now ( ) - timedelta ( days = 1 , hours = 2 )
realm . save ( update_fields = [ " date_created " ] )
with self . assertLogs ( level = " WARNING " ) as m :
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assertEqual (
m . output ,
[
f " WARNING:root:User from realm zulip attempted to access /stats, but the computed start time: { realm . date_created } (creation of realm or installation) is later than the computed end time: 0001-01-01 00:00:00+00:00 (last successful analytics update). Is the analytics cron job running? "
] ,
)
self . assert_json_error_contains ( result , " No analytics data available " )
realm . date_created = timezone_now ( ) - timedelta ( days = 1 , minutes = 10 )
realm . save ( update_fields = [ " date_created " ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assert_json_success ( result )
realm . date_created = timezone_now ( ) - timedelta ( hours = 10 )
realm . save ( update_fields = [ " date_created " ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assert_json_success ( result )
end_time = timezone_now ( ) - timedelta ( days = 5 )
fill_state = FillState . objects . create (
property = " messages_sent:is_bot:hour " , end_time = end_time , state = FillState . DONE
)
realm . date_created = timezone_now ( ) - timedelta ( days = 3 )
realm . save ( update_fields = [ " date_created " ] )
with self . assertLogs ( level = " WARNING " ) as m :
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assertEqual (
m . output ,
[
f " WARNING:root:User from realm zulip attempted to access /stats, but the computed start time: { realm . date_created } (creation of realm or installation) is later than the computed end time: { end_time } (last successful analytics update). Is the analytics cron job running? "
] ,
)
self . assert_json_error_contains ( result , " No analytics data available " )
realm . date_created = timezone_now ( ) - timedelta ( days = 1 , minutes = 10 )
realm . save ( update_fields = [ " date_created " ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assert_json_success ( result )
end_time = timezone_now ( ) - timedelta ( days = 2 )
fill_state . end_time = end_time
fill_state . save ( update_fields = [ " end_time " ] )
realm . date_created = timezone_now ( ) - timedelta ( days = 3 )
realm . save ( update_fields = [ " date_created " ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assert_json_success ( result )
realm . date_created = timezone_now ( ) - timedelta ( days = 1 , hours = 2 )
realm . save ( update_fields = [ " date_created " ] )
with self . assertLogs ( level = " WARNING " ) as m :
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assertEqual (
m . output ,
[
f " WARNING:root:User from realm zulip attempted to access /stats, but the computed start time: { realm . date_created } (creation of realm or installation) is later than the computed end time: { end_time } (last successful analytics update). Is the analytics cron job running? "
] ,
)
self . assert_json_error_contains ( result , " No analytics data available " )
realm . date_created = timezone_now ( ) - timedelta ( days = 1 , minutes = 10 )
realm . save ( update_fields = [ " date_created " ] )
result = self . client_get (
" /json/analytics/chart_data " , { " chart_name " : " messages_sent_over_time " }
)
self . assert_json_success ( result )
def test_get_chart_data_for_realm ( self ) - > None :
user = self . example_user ( " hamlet " )
self . login_user ( user )
result = self . client_get (
" /json/analytics/chart_data/realm/zulip " , { " chart_name " : " number_of_humans " }
)
self . assert_json_error ( result , " Must be an server administrator " , 400 )
user = self . example_user ( " hamlet " )
user . is_staff = True
user . save ( update_fields = [ " is_staff " ] )
stat = COUNT_STATS [ " realm_active_humans::day " ]
self . insert_data ( stat , [ None ] , [ ] )
result = self . client_get (
" /json/analytics/chart_data/realm/not_existing_realm " ,
{ " chart_name " : " number_of_humans " } ,
)
self . assert_json_error ( result , " Invalid organization " , 400 )
result = self . client_get (
" /json/analytics/chart_data/realm/zulip " , { " chart_name " : " number_of_humans " }
)
self . assert_json_success ( result )
def test_get_chart_data_for_installation ( self ) - > None :
user = self . example_user ( " hamlet " )
self . login_user ( user )
result = self . client_get (
" /json/analytics/chart_data/installation " , { " chart_name " : " number_of_humans " }
)
self . assert_json_error ( result , " Must be an server administrator " , 400 )
user = self . example_user ( " hamlet " )
user . is_staff = True
user . save ( update_fields = [ " is_staff " ] )
stat = COUNT_STATS [ " realm_active_humans::day " ]
self . insert_data ( stat , [ None ] , [ ] )
result = self . client_get (
" /json/analytics/chart_data/installation " , { " chart_name " : " number_of_humans " }
)
self . assert_json_success ( result )
class TestGetChartDataHelpers ( ZulipTestCase ) :
def test_sort_by_totals ( self ) - > None :
2024-07-12 02:30:17 +02:00
empty : list [ int ] = [ ]
2021-06-17 23:15:42 +02:00
value_arrays = { " c " : [ 0 , 1 ] , " a " : [ 9 ] , " b " : [ 1 , 1 , 1 ] , " d " : empty }
self . assertEqual ( sort_by_totals ( value_arrays ) , [ " a " , " b " , " c " , " d " ] )
def test_sort_client_labels ( self ) - > None :
data = {
" everyone " : { " a " : [ 16 ] , " c " : [ 15 ] , " b " : [ 14 ] , " e " : [ 13 ] , " d " : [ 12 ] , " h " : [ 11 ] } ,
" user " : { " a " : [ 6 ] , " b " : [ 5 ] , " d " : [ 4 ] , " e " : [ 3 ] , " f " : [ 2 ] , " g " : [ 1 ] } ,
}
self . assertEqual ( sort_client_labels ( data ) , [ " a " , " b " , " c " , " d " , " e " , " f " , " g " , " h " ] )
class TestTimeRange ( ZulipTestCase ) :
def test_time_range ( self ) - > None :
HOUR = timedelta ( hours = 1 )
DAY = timedelta ( days = 1 )
a_time = datetime ( 2016 , 3 , 14 , 22 , 59 , tzinfo = timezone . utc )
floor_hour = datetime ( 2016 , 3 , 14 , 22 , tzinfo = timezone . utc )
floor_day = datetime ( 2016 , 3 , 14 , tzinfo = timezone . utc )
# test start == end
self . assertEqual ( time_range ( a_time , a_time , CountStat . HOUR , None ) , [ ] )
self . assertEqual ( time_range ( a_time , a_time , CountStat . DAY , None ) , [ ] )
# test start == end == boundary, and min_length == 0
self . assertEqual ( time_range ( floor_hour , floor_hour , CountStat . HOUR , 0 ) , [ floor_hour ] )
self . assertEqual ( time_range ( floor_day , floor_day , CountStat . DAY , 0 ) , [ floor_day ] )
# test start and end on different boundaries
self . assertEqual (
time_range ( floor_hour , floor_hour + HOUR , CountStat . HOUR , None ) ,
[ floor_hour , floor_hour + HOUR ] ,
)
self . assertEqual (
time_range ( floor_day , floor_day + DAY , CountStat . DAY , None ) ,
[ floor_day , floor_day + DAY ] ,
)
# test min_length
self . assertEqual (
time_range ( floor_hour , floor_hour + HOUR , CountStat . HOUR , 4 ) ,
[ floor_hour - 2 * HOUR , floor_hour - HOUR , floor_hour , floor_hour + HOUR ] ,
)
self . assertEqual (
time_range ( floor_day , floor_day + DAY , CountStat . DAY , 4 ) ,
[ floor_day - 2 * DAY , floor_day - DAY , floor_day , floor_day + DAY ] ,
)
class TestMapArrays ( ZulipTestCase ) :
def test_map_arrays ( self ) - > None :
a = {
" desktop app 1.0 " : [ 1 , 2 , 3 ] ,
" desktop app 2.0 " : [ 10 , 12 , 13 ] ,
" desktop app 3.0 " : [ 21 , 22 , 23 ] ,
" website " : [ 1 , 2 , 3 ] ,
" ZulipiOS " : [ 1 , 2 , 3 ] ,
" ZulipElectron " : [ 2 , 5 , 7 ] ,
2024-05-23 16:06:02 +02:00
" ZulipMobile " : [ 1 , 2 , 3 ] ,
" ZulipMobile/flutter " : [ 1 , 1 , 1 ] ,
" ZulipFlutter " : [ 1 , 1 , 1 ] ,
2021-06-17 23:15:42 +02:00
" ZulipPython " : [ 1 , 2 , 3 ] ,
" API: Python " : [ 1 , 2 , 3 ] ,
" SomethingRandom " : [ 4 , 5 , 6 ] ,
" ZulipGitHubWebhook " : [ 7 , 7 , 9 ] ,
" ZulipAndroid " : [ 64 , 63 , 65 ] ,
2022-04-13 00:50:14 +02:00
" ZulipTerminal " : [ 9 , 10 , 11 ] ,
2021-06-17 23:15:42 +02:00
}
result = rewrite_client_arrays ( a )
self . assertEqual (
result ,
{
" Old desktop app " : [ 32 , 36 , 39 ] ,
" Old iOS app " : [ 1 , 2 , 3 ] ,
" Desktop app " : [ 2 , 5 , 7 ] ,
2024-05-23 16:06:02 +02:00
" Mobile app (React Native) " : [ 1 , 2 , 3 ] ,
" Mobile app beta (Flutter) " : [ 2 , 2 , 2 ] ,
2022-04-13 01:52:48 +02:00
" Web app " : [ 1 , 2 , 3 ] ,
2021-06-17 23:15:42 +02:00
" Python API " : [ 2 , 4 , 6 ] ,
" SomethingRandom " : [ 4 , 5 , 6 ] ,
" GitHub webhook " : [ 7 , 7 , 9 ] ,
" Old Android app " : [ 64 , 63 , 65 ] ,
2022-04-13 00:50:14 +02:00
" Terminal app " : [ 9 , 10 , 11 ] ,
2021-06-17 23:15:42 +02:00
} ,
)