invitations: Add LoggingCountStat to keep track of sent invitations.

This commit is contained in:
Rishi Gupta 2017-12-05 11:21:25 -08:00 committed by Greg Price
parent 100167fcf8
commit fbd8dde1f8
3 changed files with 57 additions and 2 deletions

View File

@ -516,6 +516,11 @@ count_stats_ = [
CountStat.DAY, interval=timedelta(days=15)-UserActivityInterval.MIN_INTERVAL_LENGTH),
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
# Must come after their dependencies.

View File

@ -19,11 +19,13 @@ from analytics.models import Anomaly, BaseCount, \
FillState, InstallationCount, RealmCount, StreamCount, \
UserCount, installation_epoch, last_successful_fill
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.models import Client, Huddle, Message, Realm, \
RealmAuditLog, Recipient, Stream, UserActivityInterval, \
UserProfile, get_client, get_user
UserProfile, get_client, get_user, PreregistrationUser
class AnalyticsTestCase(TestCase):
MINUTE = timedelta(seconds = 60)
@ -747,6 +749,45 @@ class TestLoggingCountStats(AnalyticsTestCase):
self.assertEqual(1, RealmCount.objects.filter(property=property, subgroup=False)
.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):
def test_do_drop_all_analytics_tables(self) -> None:
user = self.create_user()

View File

@ -3972,6 +3972,12 @@ def do_invite_users(user_profile: UserProfile,
raise InvitationError(_("We weren't able to invite anyone."),
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
# the PreregistrationUser objects and trigger the email invitations.
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.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)
# 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}