feat: add Usage Limits Panel component with usage tracking and visual indicators for limits
style: implement custom color schemes and gradients for account section, enhancing visual hierarchy
This commit is contained in:
168
backend/igny8_core/tasks/plan_limits.py
Normal file
168
backend/igny8_core/tasks/plan_limits.py
Normal file
@@ -0,0 +1,168 @@
|
||||
"""
|
||||
Plan Limits Reset Task
|
||||
Scheduled task to reset monthly plan limits at billing period end
|
||||
"""
|
||||
from celery import shared_task
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from igny8_core.auth.models import Account
|
||||
from igny8_core.business.billing.services.limit_service import LimitService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@shared_task(name='reset_monthly_plan_limits')
|
||||
def reset_monthly_plan_limits():
|
||||
"""
|
||||
Reset monthly plan limits for accounts whose billing period has ended.
|
||||
|
||||
This task should run daily (recommended: midnight UTC).
|
||||
It finds all accounts where the billing period has ended and resets their monthly usage.
|
||||
|
||||
Monthly limits that get reset:
|
||||
- content_ideas
|
||||
- content_words
|
||||
- images_basic
|
||||
- images_premium
|
||||
- image_prompts
|
||||
|
||||
Hard limits (sites, users, keywords, clusters) are NOT reset.
|
||||
"""
|
||||
logger.info("Starting monthly plan limits reset task")
|
||||
|
||||
today = timezone.now().date()
|
||||
reset_count = 0
|
||||
error_count = 0
|
||||
|
||||
# Find all active accounts with subscriptions
|
||||
accounts = Account.objects.filter(
|
||||
status='active',
|
||||
subscription__isnull=False
|
||||
).select_related('subscription', 'plan')
|
||||
|
||||
logger.info(f"Found {accounts.count()} active accounts with subscriptions")
|
||||
|
||||
for account in accounts:
|
||||
try:
|
||||
subscription = account.subscription
|
||||
|
||||
# Check if billing period has ended
|
||||
if subscription.current_period_end and subscription.current_period_end.date() <= today:
|
||||
logger.info(f"Resetting limits for account {account.id} ({account.name}) - "
|
||||
f"period ended {subscription.current_period_end.date()}")
|
||||
|
||||
# Reset monthly limits
|
||||
result = LimitService.reset_monthly_limits(account)
|
||||
|
||||
# Update subscription period
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
# Calculate new period based on billing cycle
|
||||
plan = account.plan
|
||||
if plan.billing_cycle == 'monthly':
|
||||
new_period_start = subscription.current_period_end + timedelta(days=1)
|
||||
new_period_end = new_period_start + relativedelta(months=1) - timedelta(days=1)
|
||||
elif plan.billing_cycle == 'annual':
|
||||
new_period_start = subscription.current_period_end + timedelta(days=1)
|
||||
new_period_end = new_period_start + relativedelta(years=1) - timedelta(days=1)
|
||||
else:
|
||||
# Default to monthly
|
||||
new_period_start = subscription.current_period_end + timedelta(days=1)
|
||||
new_period_end = new_period_start + relativedelta(months=1) - timedelta(days=1)
|
||||
|
||||
# Update subscription
|
||||
subscription.current_period_start = new_period_start
|
||||
subscription.current_period_end = new_period_end
|
||||
subscription.save(update_fields=['current_period_start', 'current_period_end'])
|
||||
|
||||
reset_count += 1
|
||||
|
||||
logger.info(f"Reset complete for account {account.id}: "
|
||||
f"New period {new_period_start} to {new_period_end}")
|
||||
|
||||
except Exception as e:
|
||||
error_count += 1
|
||||
logger.error(f"Error resetting limits for account {account.id}: {str(e)}", exc_info=True)
|
||||
|
||||
logger.info(f"Monthly plan limits reset task complete: "
|
||||
f"{reset_count} accounts reset, {error_count} errors")
|
||||
|
||||
return {
|
||||
'reset_count': reset_count,
|
||||
'error_count': error_count,
|
||||
'total_accounts': accounts.count(),
|
||||
}
|
||||
|
||||
|
||||
@shared_task(name='check_approaching_limits')
|
||||
def check_approaching_limits(threshold_percentage=80):
|
||||
"""
|
||||
Check for accounts approaching their plan limits and send notifications.
|
||||
|
||||
Args:
|
||||
threshold_percentage: Percentage at which to trigger warning (default 80%)
|
||||
|
||||
This task should run daily.
|
||||
It checks both hard and monthly limits and sends warnings when usage exceeds threshold.
|
||||
"""
|
||||
logger.info(f"Starting limit warning check task (threshold: {threshold_percentage}%)")
|
||||
|
||||
warning_count = 0
|
||||
|
||||
# Find all active accounts
|
||||
accounts = Account.objects.filter(status='active').select_related('plan')
|
||||
|
||||
for account in accounts:
|
||||
try:
|
||||
if not account.plan:
|
||||
continue
|
||||
|
||||
# Get usage summary
|
||||
summary = LimitService.get_usage_summary(account)
|
||||
|
||||
warnings = []
|
||||
|
||||
# Check hard limits
|
||||
for limit_type, data in summary.get('hard_limits', {}).items():
|
||||
if data['percentage_used'] >= threshold_percentage:
|
||||
warnings.append({
|
||||
'type': 'hard',
|
||||
'limit_type': limit_type,
|
||||
'display_name': data['display_name'],
|
||||
'current': data['current'],
|
||||
'limit': data['limit'],
|
||||
'percentage': data['percentage_used'],
|
||||
})
|
||||
|
||||
# Check monthly limits
|
||||
for limit_type, data in summary.get('monthly_limits', {}).items():
|
||||
if data['percentage_used'] >= threshold_percentage:
|
||||
warnings.append({
|
||||
'type': 'monthly',
|
||||
'limit_type': limit_type,
|
||||
'display_name': data['display_name'],
|
||||
'current': data['current'],
|
||||
'limit': data['limit'],
|
||||
'percentage': data['percentage_used'],
|
||||
'resets_in_days': summary.get('days_until_reset', 0),
|
||||
})
|
||||
|
||||
if warnings:
|
||||
warning_count += 1
|
||||
logger.info(f"Account {account.id} ({account.name}) has {len(warnings)} limit warnings")
|
||||
|
||||
# TODO: Send email notification
|
||||
# from igny8_core.business.billing.services.email_service import send_limit_warning_email
|
||||
# send_limit_warning_email(account, warnings)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking limits for account {account.id}: {str(e)}", exc_info=True)
|
||||
|
||||
logger.info(f"Limit warning check complete: {warning_count} accounts with warnings")
|
||||
|
||||
return {
|
||||
'warning_count': warning_count,
|
||||
'total_accounts': accounts.count(),
|
||||
}
|
||||
Reference in New Issue
Block a user