mirror of https://github.com/zulip/zulip.git
invitations: Add LoggingCountStat to keep track of sent invitations.
This commit is contained in:
parent
100167fcf8
commit
fbd8dde1f8
|
@ -516,6 +516,11 @@ count_stats_ = [
|
||||||
CountStat.DAY, interval=timedelta(days=15)-UserActivityInterval.MIN_INTERVAL_LENGTH),
|
CountStat.DAY, interval=timedelta(days=15)-UserActivityInterval.MIN_INTERVAL_LENGTH),
|
||||||
CountStat('minutes_active::day', DataCollector(UserCount, do_pull_minutes_active), CountStat.DAY),
|
CountStat('minutes_active::day', DataCollector(UserCount, do_pull_minutes_active), CountStat.DAY),
|
||||||
|
|
||||||
|
# Rate limiting stats
|
||||||
|
|
||||||
|
# Used to limit the number of invitation emails sent by a realm
|
||||||
|
LoggingCountStat('invites_sent::day', RealmCount, CountStat.DAY),
|
||||||
|
|
||||||
# Dependent stats
|
# Dependent stats
|
||||||
# Must come after their dependencies.
|
# Must come after their dependencies.
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,13 @@ from analytics.models import Anomaly, BaseCount, \
|
||||||
FillState, InstallationCount, RealmCount, StreamCount, \
|
FillState, InstallationCount, RealmCount, StreamCount, \
|
||||||
UserCount, installation_epoch, last_successful_fill
|
UserCount, installation_epoch, last_successful_fill
|
||||||
from zerver.lib.actions import do_activate_user, do_create_user, \
|
from zerver.lib.actions import do_activate_user, do_create_user, \
|
||||||
do_deactivate_user, do_reactivate_user, update_user_activity_interval
|
do_deactivate_user, do_reactivate_user, update_user_activity_interval, \
|
||||||
|
do_invite_users, do_revoke_user_invite, do_resend_user_invite_email, \
|
||||||
|
InvitationError
|
||||||
from zerver.lib.timestamp import TimezoneNotUTCException, floor_to_day
|
from zerver.lib.timestamp import TimezoneNotUTCException, floor_to_day
|
||||||
from zerver.models import Client, Huddle, Message, Realm, \
|
from zerver.models import Client, Huddle, Message, Realm, \
|
||||||
RealmAuditLog, Recipient, Stream, UserActivityInterval, \
|
RealmAuditLog, Recipient, Stream, UserActivityInterval, \
|
||||||
UserProfile, get_client, get_user
|
UserProfile, get_client, get_user, PreregistrationUser
|
||||||
|
|
||||||
class AnalyticsTestCase(TestCase):
|
class AnalyticsTestCase(TestCase):
|
||||||
MINUTE = timedelta(seconds = 60)
|
MINUTE = timedelta(seconds = 60)
|
||||||
|
@ -747,6 +749,45 @@ class TestLoggingCountStats(AnalyticsTestCase):
|
||||||
self.assertEqual(1, RealmCount.objects.filter(property=property, subgroup=False)
|
self.assertEqual(1, RealmCount.objects.filter(property=property, subgroup=False)
|
||||||
.aggregate(Sum('value'))['value__sum'])
|
.aggregate(Sum('value'))['value__sum'])
|
||||||
|
|
||||||
|
def test_invites_sent(self) -> None:
|
||||||
|
property = 'invites_sent::day'
|
||||||
|
|
||||||
|
def assertInviteCountEquals(count: int) -> None:
|
||||||
|
self.assertEqual(count, RealmCount.objects.filter(property=property, subgroup=None)
|
||||||
|
.aggregate(Sum('value'))['value__sum'])
|
||||||
|
|
||||||
|
user = self.create_user(email='first@domain.tld')
|
||||||
|
stream, _ = self.create_stream_with_recipient()
|
||||||
|
do_invite_users(user, ['user1@domain.tld', 'user2@domain.tld'], [stream])
|
||||||
|
assertInviteCountEquals(2)
|
||||||
|
|
||||||
|
# We currently send emails when re-inviting users that haven't
|
||||||
|
# turned into accounts, so count them towards the total
|
||||||
|
do_invite_users(user, ['user1@domain.tld', 'user2@domain.tld'], [stream])
|
||||||
|
assertInviteCountEquals(4)
|
||||||
|
|
||||||
|
# Test mix of good and malformed invite emails
|
||||||
|
try:
|
||||||
|
do_invite_users(user, ['user3@domain.tld', 'malformed'], [stream])
|
||||||
|
except InvitationError:
|
||||||
|
pass
|
||||||
|
assertInviteCountEquals(4)
|
||||||
|
|
||||||
|
# Test inviting existing users
|
||||||
|
try:
|
||||||
|
do_invite_users(user, ['first@domain.tld', 'user4@domain.tld'], [stream])
|
||||||
|
except InvitationError:
|
||||||
|
pass
|
||||||
|
assertInviteCountEquals(5)
|
||||||
|
|
||||||
|
# Revoking invite should not give you credit
|
||||||
|
do_revoke_user_invite(PreregistrationUser.objects.filter(realm=user.realm).first())
|
||||||
|
assertInviteCountEquals(5)
|
||||||
|
|
||||||
|
# Resending invite should cost you
|
||||||
|
do_resend_user_invite_email(PreregistrationUser.objects.first())
|
||||||
|
assertInviteCountEquals(6)
|
||||||
|
|
||||||
class TestDeleteStats(AnalyticsTestCase):
|
class TestDeleteStats(AnalyticsTestCase):
|
||||||
def test_do_drop_all_analytics_tables(self) -> None:
|
def test_do_drop_all_analytics_tables(self) -> None:
|
||||||
user = self.create_user()
|
user = self.create_user()
|
||||||
|
|
|
@ -3972,6 +3972,12 @@ def do_invite_users(user_profile: UserProfile,
|
||||||
raise InvitationError(_("We weren't able to invite anyone."),
|
raise InvitationError(_("We weren't able to invite anyone."),
|
||||||
skipped, sent_invitations=False)
|
skipped, sent_invitations=False)
|
||||||
|
|
||||||
|
# We do this here rather than in the invite queue processor since this
|
||||||
|
# is used for rate limiting invitations, rather than keeping track of
|
||||||
|
# when exactly invitations were sent
|
||||||
|
do_increment_logging_stat(user_profile.realm, COUNT_STATS['invites_sent::day'],
|
||||||
|
None, timezone_now(), increment=len(validated_emails))
|
||||||
|
|
||||||
# Now that we are past all the possible errors, we actually create
|
# Now that we are past all the possible errors, we actually create
|
||||||
# the PreregistrationUser objects and trigger the email invitations.
|
# the PreregistrationUser objects and trigger the email invitations.
|
||||||
for email in validated_emails:
|
for email in validated_emails:
|
||||||
|
@ -4030,6 +4036,9 @@ def do_resend_user_invite_email(prereg_user: PreregistrationUser) -> str:
|
||||||
prereg_user.invited_at = timezone_now()
|
prereg_user.invited_at = timezone_now()
|
||||||
prereg_user.save()
|
prereg_user.save()
|
||||||
|
|
||||||
|
do_increment_logging_stat(prereg_user.realm, COUNT_STATS['invites_sent::day'],
|
||||||
|
None, prereg_user.invited_at)
|
||||||
|
|
||||||
clear_scheduled_invitation_emails(prereg_user.email)
|
clear_scheduled_invitation_emails(prereg_user.email)
|
||||||
# We don't store the custom email body, so just set it to None
|
# We don't store the custom email body, so just set it to None
|
||||||
event = {"prereg_id": prereg_user.id, "referrer_id": prereg_user.referred_by.id, "email_body": None}
|
event = {"prereg_id": prereg_user.id, "referrer_id": prereg_user.referred_by.id, "email_body": None}
|
||||||
|
|
Loading…
Reference in New Issue