privaterelay/management/commands/update_phone_remaining_stats.py (72 lines of code) (raw):
import logging
from datetime import UTC, datetime, timedelta
from django.conf import settings
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from privaterelay.management.utils import (
get_free_phone_social_accounts,
get_phone_subscriber_social_accounts,
)
from privaterelay.models import Profile
if settings.PHONES_ENABLED:
from phones.models import RelayNumber
logger = logging.getLogger("events")
def reset_phone_remaining_stats(user: User) -> None:
# re-set remaining_texts and remaining_seconds to the maximum value
try:
relay_number = RelayNumber.objects.get(user=user)
except RelayNumber.DoesNotExist:
# no RelayNumber set, do nothing
return
relay_number.remaining_texts = settings.MAX_TEXTS_PER_BILLING_CYCLE
relay_number.remaining_seconds = settings.MAX_MINUTES_PER_BILLING_CYCLE * 60
relay_number.save()
def get_next_reset_date(profile: Profile) -> datetime:
# TODO: consider moving this as a property in Profile model
# assumes that profile being passed have already been checked to have
# phone subscription or a free phone user
if profile.date_phone_subscription_reset is None:
# there is a problem with the sync_phone_related_dates_on_profile
# or a new foxfooder whose date_phone_subscription_reset did not get set in
if profile.fxa:
fxa_uid = profile.fxa.uid
else:
fxa_uid = "None"
logger.error(
"phone_user_profile_dates_not_set",
extra={
"fxa_uid": fxa_uid,
"date_subscribed_phone": profile.date_phone_subscription_end,
"date_phone_subscription_start": profile.date_phone_subscription_start,
"date_phone_subscription_reset": profile.date_phone_subscription_reset,
"date_phone_subscription_end": profile.date_phone_subscription_end,
},
)
return datetime.now(UTC) - timedelta(minutes=15)
calculated_next_reset_date = profile.date_phone_subscription_reset + timedelta(
settings.MAX_DAYS_IN_MONTH
)
if profile.date_phone_subscription_end is None:
return calculated_next_reset_date
if profile.date_phone_subscription_end < calculated_next_reset_date:
# return the past or the closest next reset date
return profile.date_phone_subscription_end
return calculated_next_reset_date
def update_phone_remaining_stats() -> tuple[int, int]:
social_accounts_with_phones = get_phone_subscriber_social_accounts()
free_phones_social_accounts = get_free_phone_social_accounts()
social_accounts_with_phones.update(free_phones_social_accounts)
if not settings.PHONES_ENABLED or len(social_accounts_with_phones) == 0:
return 0, 0
updated_profiles = []
datetime_now = datetime.now(UTC)
for social_account in social_accounts_with_phones:
profile = social_account.user.profile
next_reset_date = get_next_reset_date(profile)
if next_reset_date > datetime_now:
continue
# next reset day is now or in the past
reset_phone_remaining_stats(profile.user)
profile.date_phone_subscription_reset = datetime_now
profile.save()
updated_profiles.append(profile)
return len(social_accounts_with_phones), len(updated_profiles)
class Command(BaseCommand):
help = "Update all phone users' subscription and stats."
def handle(self, *args, **options):
num_profiles_w_phones, num_profiles_updated = update_phone_remaining_stats()
print(
f"Out of {num_profiles_w_phones} profiles,"
f" {num_profiles_updated} limits were reset"
)