Enhancement: Optimize profile expiration handling and update task naming for clarity

This commit is contained in:
SergeantPanda 2026-03-15 12:42:34 -05:00
parent b22f9e8e07
commit 1d4603dadd
4 changed files with 22 additions and 14 deletions

View file

@ -40,7 +40,7 @@ class M3UAccountViewSet(viewsets.ModelViewSet):
queryset = M3UAccount.objects.select_related(
"refresh_task__crontab", "refresh_task__interval"
).prefetch_related("channel_group")
).prefetch_related("channel_group", "profiles")
serializer_class = M3UAccountSerializer
def get_permissions(self):

View file

@ -212,8 +212,9 @@ class M3UAccountSerializer(serializers.ModelSerializer):
cron_expr = f"{ct.minute} {ct.hour} {ct.day_of_month} {ct.month_of_year} {ct.day_of_week}"
data["cron_expression"] = cron_expr
# Surface default profile's exp_date for the form
default_profile = instance.profiles.filter(is_default=True).first()
# Surface default profile's exp_date for the form.
# Use prefetch cache (obj.profiles.all()) to avoid an extra query per account.
default_profile = next((p for p in instance.profiles.all() if p.is_default), None)
data["exp_date"] = (
default_profile.exp_date.isoformat() if default_profile and default_profile.exp_date else None
)
@ -337,15 +338,19 @@ class M3UAccountSerializer(serializers.ModelSerializer):
def get_earliest_expiration(self, obj):
"""Return the soonest exp_date across all active profiles for this account."""
profiles_with_exp = obj.profiles.filter(
is_active=True, exp_date__isnull=False
).order_by("exp_date")
first = profiles_with_exp.first()
return first.exp_date.isoformat() if first else None
# Filter in Python over the prefetch cache to avoid an extra query per account.
expiring = [p.exp_date for p in obj.profiles.all() if p.is_active and p.exp_date]
if not expiring:
return None
return min(expiring).isoformat()
def get_all_expirations(self, obj):
"""Return exp_date info for every profile that has one (for tooltip)."""
profiles = obj.profiles.filter(exp_date__isnull=False).order_by("exp_date")
# Filter in Python over the prefetch cache to avoid an extra query per account.
profiles = sorted(
(p for p in obj.profiles.all() if p.exp_date),
key=lambda p: p.exp_date,
)
return [
{
"profile_id": p.id,

View file

@ -3199,9 +3199,12 @@ def evaluate_profile_expiration_notification(profile):
from core.models import SystemNotification
from core.utils import send_websocket_notification, send_notification_dismissed
exp = profile.exp_date
if not exp:
return None
now = timezone.now()
warning_threshold = now + timezone.timedelta(days=7)
exp = profile.exp_date
warning_key = f"xc-exp-warning-{profile.id}"
expired_key = f"xc-exp-expired-{profile.id}"
@ -3299,7 +3302,7 @@ def evaluate_profile_expiration_notification(profile):
@shared_task
def check_xc_account_expirations():
def check_account_expirations():
"""
Daily task: check all account profiles for upcoming expirations.
Creates/updates SystemNotifications for profiles expiring within 7 days.

View file

@ -267,9 +267,9 @@ CELERY_BEAT_SCHEDULE = {
"task": "core.tasks.check_for_version_update",
"schedule": 86400.0, # Once every 24 hours
},
# Check for XC account expirations daily
"check-xc-account-expirations": {
"task": "apps.m3u.tasks.check_xc_account_expirations",
# Check for account expirations daily
"check-account-expirations": {
"task": "apps.m3u.tasks.check_account_expirations",
"schedule": 86400.0, # Once every 24 hours
},
}