2016-12-20 02:30:08 +01:00
|
|
|
from datetime import datetime, timedelta
|
2017-11-16 00:55:49 +01:00
|
|
|
from typing import Dict, List, Optional
|
|
|
|
|
2017-02-10 21:52:14 +01:00
|
|
|
import mock
|
2017-11-16 00:55:49 +01:00
|
|
|
from django.utils.timezone import utc
|
2017-02-10 21:52:14 +01:00
|
|
|
|
2017-11-16 00:55:49 +01:00
|
|
|
from analytics.lib.counts import COUNT_STATS, CountStat
|
|
|
|
from analytics.lib.time_utils import time_range
|
|
|
|
from analytics.models import FillState, \
|
|
|
|
RealmCount, UserCount, last_successful_fill
|
|
|
|
from analytics.views import get_chart_data, rewrite_client_arrays, \
|
|
|
|
sort_by_totals, sort_client_labels, stats
|
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
|
|
from zerver.lib.timestamp import ceiling_to_day, \
|
|
|
|
ceiling_to_hour, datetime_to_timestamp
|
|
|
|
from zerver.models import Client, get_realm
|
2017-02-10 21:52:14 +01:00
|
|
|
|
|
|
|
class TestStatsEndpoint(ZulipTestCase):
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_stats(self) -> None:
|
2017-07-13 20:22:26 +02:00
|
|
|
self.user = self.example_user('hamlet')
|
2017-02-10 21:52:14 +01:00
|
|
|
self.login(self.user.email)
|
|
|
|
result = self.client_get('/stats')
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
# Check that we get something back
|
2017-07-09 17:14:14 +02:00
|
|
|
self.assert_in_response("Zulip analytics for", result)
|
2017-02-10 21:52:14 +01:00
|
|
|
|
2018-10-31 21:09:33 +01:00
|
|
|
def test_guest_user_cant_access_stats(self) -> None:
|
|
|
|
self.user = self.example_user('polonius')
|
|
|
|
self.login(self.user.email)
|
|
|
|
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)
|
|
|
|
|
2018-04-15 18:43:48 +02:00
|
|
|
def test_stats_for_realm(self) -> None:
|
|
|
|
user_profile = self.example_user('hamlet')
|
|
|
|
self.login(user_profile.email)
|
|
|
|
|
|
|
|
result = self.client_get('/stats/realm/zulip/')
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
user_profile = self.example_user('hamlet')
|
|
|
|
user_profile.is_staff = True
|
|
|
|
user_profile.save(update_fields=['is_staff'])
|
|
|
|
|
|
|
|
result = self.client_get('/stats/realm/not_existing_realm/')
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
result = self.client_get('/stats/realm/zulip/')
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
self.assert_in_response("Zulip analytics for", result)
|
|
|
|
|
2018-05-18 02:16:29 +02:00
|
|
|
def test_stats_for_installation(self) -> None:
|
|
|
|
user_profile = self.example_user('hamlet')
|
|
|
|
self.login(user_profile.email)
|
|
|
|
|
|
|
|
result = self.client_get('/stats/installation')
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
user_profile = self.example_user('hamlet')
|
|
|
|
user_profile.is_staff = True
|
|
|
|
user_profile.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)
|
|
|
|
|
2017-02-10 21:52:14 +01:00
|
|
|
class TestGetChartData(ZulipTestCase):
|
2017-11-05 06:54:00 +01:00
|
|
|
def setUp(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
self.realm = get_realm('zulip')
|
2017-07-13 20:22:26 +02:00
|
|
|
self.user = self.example_user('hamlet')
|
2017-02-10 21:52:14 +01:00
|
|
|
self.login(self.user.email)
|
|
|
|
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)]
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def data(self, i: int) -> List[int]:
|
2017-02-10 21:52:14 +01:00
|
|
|
return [0, 0, i, 0]
|
|
|
|
|
2017-11-17 22:19:48 +01:00
|
|
|
def insert_data(self, stat: CountStat, realm_subgroups: List[Optional[str]],
|
|
|
|
user_subgroups: List[str]) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
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)])
|
|
|
|
FillState.objects.create(property=stat.property, end_time=fill_time, state=FillState.DONE)
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_number_of_humans(self) -> None:
|
2017-04-25 23:54:30 +02:00
|
|
|
stat = COUNT_STATS['realm_active_humans::day']
|
|
|
|
self.insert_data(stat, [None], [])
|
2018-05-19 22:43:02 +02:00
|
|
|
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'], [])
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'number_of_humans'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data, {
|
|
|
|
'msg': '',
|
|
|
|
'end_times': [datetime_to_timestamp(dt) for dt in self.end_times_day],
|
|
|
|
'frequency': CountStat.DAY,
|
2018-05-19 22:43:02 +02:00
|
|
|
'everyone': {'_1day': self.data(100), '_15day': self.data(100), 'all_time': self.data(100)},
|
2017-02-10 21:52:14 +01:00
|
|
|
'display_order': None,
|
|
|
|
'result': 'success',
|
|
|
|
})
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_messages_sent_over_time(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
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'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data, {
|
|
|
|
'msg': '',
|
|
|
|
'end_times': [datetime_to_timestamp(dt) for dt in self.end_times_hour],
|
|
|
|
'frequency': CountStat.HOUR,
|
2018-05-18 22:13:08 +02:00
|
|
|
'everyone': {'bot': self.data(100), 'human': self.data(101)},
|
2017-03-25 21:48:37 +01:00
|
|
|
'user': {'bot': self.data(0), 'human': self.data(200)},
|
2017-02-10 21:52:14 +01:00
|
|
|
'display_order': None,
|
|
|
|
'result': 'success',
|
|
|
|
})
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_messages_sent_by_message_type(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
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'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data, {
|
|
|
|
'msg': '',
|
|
|
|
'end_times': [datetime_to_timestamp(dt) for dt in self.end_times_day],
|
|
|
|
'frequency': CountStat.DAY,
|
2018-05-18 22:13:08 +02:00
|
|
|
'everyone': {'Public streams': self.data(100), 'Private streams': self.data(0),
|
|
|
|
'Private messages': self.data(101), 'Group private messages': self.data(0)},
|
2017-03-20 19:38:58 +01:00
|
|
|
'user': {'Public streams': self.data(200), 'Private streams': self.data(201),
|
|
|
|
'Private messages': self.data(0), 'Group private messages': self.data(0)},
|
|
|
|
'display_order': ['Private messages', 'Public streams', 'Private streams', 'Group private messages'],
|
2017-02-10 21:52:14 +01:00
|
|
|
'result': 'success',
|
|
|
|
})
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_messages_sent_by_client(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
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')
|
|
|
|
self.insert_data(stat, [client4.id, client3.id, client2.id],
|
2017-04-12 07:25:53 +02:00
|
|
|
[client3.id, client1.id])
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'messages_sent_by_client'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data, {
|
|
|
|
'msg': '',
|
|
|
|
'end_times': [datetime_to_timestamp(dt) for dt in self.end_times_day],
|
|
|
|
'frequency': CountStat.DAY,
|
2018-05-18 22:13:08 +02:00
|
|
|
'everyone': {'client 4': self.data(100), 'client 3': self.data(101),
|
|
|
|
'client 2': self.data(102)},
|
2017-04-12 07:25:53 +02:00
|
|
|
'user': {'client 3': self.data(200), 'client 1': self.data(201)},
|
2017-02-10 21:52:14 +01:00
|
|
|
'display_order': ['client 1', 'client 2', 'client 3', 'client 4'],
|
|
|
|
'result': 'success',
|
|
|
|
})
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_include_empty_subgroups(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
FillState.objects.create(
|
2017-11-17 22:19:48 +01:00
|
|
|
property='realm_active_humans::day', end_time=self.end_times_day[0],
|
|
|
|
state=FillState.DONE)
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'number_of_humans'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2018-05-19 22:43:02 +02:00
|
|
|
self.assertEqual(data['everyone'], {"_1day": [0], "_15day": [0], "all_time": [0]})
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertFalse('user' in data)
|
|
|
|
|
|
|
|
FillState.objects.create(
|
2017-11-17 22:19:48 +01:00
|
|
|
property='messages_sent:is_bot:hour', end_time=self.end_times_hour[0],
|
|
|
|
state=FillState.DONE)
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'messages_sent_over_time'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2018-05-18 22:13:08 +02:00
|
|
|
self.assertEqual(data['everyone'], {'human': [0], 'bot': [0]})
|
2017-03-25 21:48:37 +01:00
|
|
|
self.assertEqual(data['user'], {'human': [0], 'bot': [0]})
|
2017-02-10 21:52:14 +01:00
|
|
|
|
|
|
|
FillState.objects.create(
|
2017-11-17 22:19:48 +01:00
|
|
|
property='messages_sent:message_type:day', end_time=self.end_times_day[0],
|
|
|
|
state=FillState.DONE)
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'messages_sent_by_message_type'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2018-05-18 22:13:08 +02:00
|
|
|
self.assertEqual(data['everyone'], {
|
2017-11-17 22:19:48 +01:00
|
|
|
'Public streams': [0], 'Private streams': [0],
|
|
|
|
'Private messages': [0], 'Group private messages': [0]})
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data['user'], {
|
2017-11-17 22:19:48 +01:00
|
|
|
'Public streams': [0], 'Private streams': [0],
|
|
|
|
'Private messages': [0], 'Group private messages': [0]})
|
2017-02-10 21:52:14 +01:00
|
|
|
|
|
|
|
FillState.objects.create(
|
2017-11-17 22:19:48 +01:00
|
|
|
property='messages_sent:client:day', end_time=self.end_times_day[0],
|
|
|
|
state=FillState.DONE)
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'messages_sent_by_client'})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2018-05-18 22:13:08 +02:00
|
|
|
self.assertEqual(data['everyone'], {})
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data['user'], {})
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_start_and_end(self) -> None:
|
2017-04-25 23:54:30 +02:00
|
|
|
stat = COUNT_STATS['realm_active_humans::day']
|
|
|
|
self.insert_data(stat, [None], [])
|
2018-05-19 22:43:02 +02:00
|
|
|
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'], [])
|
2017-02-10 21:52:14 +01:00
|
|
|
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]})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data['end_times'], end_time_timestamps[1:3])
|
2018-05-19 22:43:02 +02:00
|
|
|
self.assertEqual(data['everyone'], {'_1day': [0, 100], '_15day': [0, 100], 'all_time': [0, 100]})
|
2017-02-10 21:52:14 +01:00
|
|
|
|
|
|
|
# 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')
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_min_length(self) -> None:
|
2017-04-25 23:54:30 +02:00
|
|
|
stat = COUNT_STATS['realm_active_humans::day']
|
|
|
|
self.insert_data(stat, [None], [])
|
2018-05-19 22:43:02 +02:00
|
|
|
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'], [])
|
2017-02-10 21:52:14 +01:00
|
|
|
# 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})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertEqual(data['end_times'], [datetime_to_timestamp(dt) for dt in self.end_times_day])
|
2018-05-19 22:43:02 +02:00
|
|
|
self.assertEqual(data['everyone'], {'_1day': self.data(100), '_15day': self.data(100), 'all_time': self.data(100)})
|
2017-02-10 21:52:14 +01:00
|
|
|
# test min_length larger than filled data
|
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'number_of_humans',
|
|
|
|
'min_length': 5})
|
|
|
|
self.assert_json_success(result)
|
2017-08-17 08:26:42 +02:00
|
|
|
data = result.json()
|
2017-02-10 21:52:14 +01: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])
|
2018-05-19 22:43:02 +02:00
|
|
|
self.assertEqual(data['everyone'], {'_1day': [0]+self.data(100), '_15day': [0]+self.data(100), 'all_time': [0]+self.data(100)})
|
2017-02-10 21:52:14 +01:00
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_non_existent_chart(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'does_not_exist'})
|
|
|
|
self.assert_json_error_contains(result, 'Unknown chart name')
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_analytics_not_running(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
# try to get data for a valid chart, but before we've put anything in the database
|
|
|
|
# (e.g. before update_analytics_counts has been run)
|
|
|
|
with mock.patch('logging.warning'):
|
|
|
|
result = self.client_get('/json/analytics/chart_data',
|
|
|
|
{'chart_name': 'number_of_humans'})
|
|
|
|
self.assert_json_error_contains(result, 'No analytics data available')
|
|
|
|
|
2018-04-15 18:43:48 +02:00
|
|
|
def test_get_chart_data_for_realm(self) -> None:
|
|
|
|
user_profile = self.example_user('hamlet')
|
|
|
|
self.login(user_profile.email)
|
|
|
|
|
|
|
|
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_profile = self.example_user('hamlet')
|
|
|
|
user_profile.is_staff = True
|
|
|
|
user_profile.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)
|
|
|
|
|
2018-05-18 02:16:29 +02:00
|
|
|
def test_get_chart_data_for_installation(self) -> None:
|
|
|
|
user_profile = self.example_user('hamlet')
|
|
|
|
self.login(user_profile.email)
|
|
|
|
|
|
|
|
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_profile = self.example_user('hamlet')
|
|
|
|
user_profile.is_staff = True
|
|
|
|
user_profile.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)
|
|
|
|
|
2017-02-10 21:52:14 +01:00
|
|
|
class TestGetChartDataHelpers(ZulipTestCase):
|
|
|
|
# last_successful_fill is in analytics/models.py, but get_chart_data is
|
|
|
|
# the only function that uses it at the moment
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_last_successful_fill(self) -> None:
|
2017-02-10 21:52:14 +01:00
|
|
|
self.assertIsNone(last_successful_fill('non-existant'))
|
|
|
|
a_time = datetime(2016, 3, 14, 19).replace(tzinfo=utc)
|
|
|
|
one_hour_before = datetime(2016, 3, 14, 18).replace(tzinfo=utc)
|
|
|
|
fillstate = FillState.objects.create(property='property', end_time=a_time,
|
|
|
|
state=FillState.DONE)
|
|
|
|
self.assertEqual(last_successful_fill('property'), a_time)
|
|
|
|
fillstate.state = FillState.STARTED
|
|
|
|
fillstate.save()
|
|
|
|
self.assertEqual(last_successful_fill('property'), one_hour_before)
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_sort_by_totals(self) -> None:
|
2017-05-31 21:15:43 +02:00
|
|
|
empty = [] # type: List[int]
|
2017-02-10 21:52:14 +01: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'])
|
|
|
|
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_sort_client_labels(self) -> None:
|
2018-05-18 22:13:08 +02:00
|
|
|
data = {'everyone': {'a': [16], 'c': [15], 'b': [14], 'e': [13], 'd': [12], 'h': [11]},
|
2017-02-10 21:52:14 +01:00
|
|
|
'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'])
|
2016-12-20 02:30:08 +01:00
|
|
|
|
|
|
|
class TestTimeRange(ZulipTestCase):
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_time_range(self) -> None:
|
2016-12-20 02:30:08 +01:00
|
|
|
HOUR = timedelta(hours=1)
|
|
|
|
DAY = timedelta(days=1)
|
|
|
|
|
2017-10-05 01:51:49 +02:00
|
|
|
a_time = datetime(2016, 3, 14, 22, 59).replace(tzinfo=utc)
|
|
|
|
floor_hour = datetime(2016, 3, 14, 22).replace(tzinfo=utc)
|
|
|
|
floor_day = datetime(2016, 3, 14).replace(tzinfo=utc)
|
2016-12-20 02:30:08 +01:00
|
|
|
|
|
|
|
# test start == end
|
2017-02-02 01:29:58 +01:00
|
|
|
self.assertEqual(time_range(a_time, a_time, CountStat.HOUR, None), [])
|
|
|
|
self.assertEqual(time_range(a_time, a_time, CountStat.DAY, None), [])
|
2016-12-20 02:30:08 +01:00
|
|
|
# test start == end == boundary, and min_length == 0
|
2017-02-02 01:29:58 +01:00
|
|
|
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])
|
2016-12-20 02:30:08 +01:00
|
|
|
# test start and end on different boundaries
|
2017-02-02 01:29:58 +01:00
|
|
|
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])
|
2016-12-20 02:30:08 +01:00
|
|
|
# test min_length
|
2017-02-02 01:29:58 +01:00
|
|
|
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])
|
2017-02-06 01:17:31 +01:00
|
|
|
|
|
|
|
class TestMapArrays(ZulipTestCase):
|
2017-11-05 06:54:00 +01:00
|
|
|
def test_map_arrays(self) -> None:
|
2017-02-06 01:17:31 +01:00
|
|
|
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],
|
2017-07-08 03:31:47 +02:00
|
|
|
'ZulipElectron': [2, 5, 7],
|
2017-02-06 01:17:31 +01:00
|
|
|
'ZulipMobile': [1, 5, 7],
|
|
|
|
'ZulipPython': [1, 2, 3],
|
|
|
|
'API: Python': [1, 2, 3],
|
|
|
|
'SomethingRandom': [4, 5, 6],
|
|
|
|
'ZulipGitHubWebhook': [7, 7, 9],
|
|
|
|
'ZulipAndroid': [64, 63, 65]}
|
|
|
|
result = rewrite_client_arrays(a)
|
|
|
|
self.assertEqual(result,
|
|
|
|
{'Old desktop app': [32, 36, 39],
|
|
|
|
'Old iOS app': [1, 2, 3],
|
2017-07-08 03:31:47 +02:00
|
|
|
'Desktop app': [2, 5, 7],
|
2017-07-08 03:31:13 +02:00
|
|
|
'Mobile app': [1, 5, 7],
|
2017-02-06 01:17:31 +01:00
|
|
|
'Website': [1, 2, 3],
|
|
|
|
'Python API': [2, 4, 6],
|
|
|
|
'SomethingRandom': [4, 5, 6],
|
|
|
|
'GitHub webhook': [7, 7, 9],
|
2017-10-03 20:59:41 +02:00
|
|
|
'Old Android app': [64, 63, 65]})
|