diff --git a/backend/igny8_core/business/billing/management/commands/backfill_usage.py b/backend/igny8_core/business/billing/management/commands/backfill_usage.py new file mode 100644 index 00000000..53d6dab0 --- /dev/null +++ b/backend/igny8_core/business/billing/management/commands/backfill_usage.py @@ -0,0 +1,87 @@ +""" +Management command to backfill usage tracking for existing content. +Usage: python manage.py backfill_usage [account_id] +""" +from django.core.management.base import BaseCommand +from django.apps import apps +from django.db import transaction +from igny8_core.auth.models import Account + + +class Command(BaseCommand): + help = 'Backfill usage tracking for existing content' + + def add_arguments(self, parser): + parser.add_argument( + 'account_id', + nargs='?', + type=int, + help='Account ID to backfill (optional, processes all accounts if not provided)' + ) + + def handle(self, *args, **options): + account_id = options.get('account_id') + + if account_id: + accounts = Account.objects.filter(id=account_id).select_related('plan') + if not accounts.exists(): + self.stdout.write(self.style.ERROR(f'Account {account_id} not found')) + return + else: + accounts = Account.objects.filter(plan__isnull=False).select_related('plan') + + ContentIdeas = apps.get_model('planner', 'ContentIdeas') + Content = apps.get_model('writer', 'Content') + Images = apps.get_model('writer', 'Images') + + total_accounts = accounts.count() + self.stdout.write(f'Processing {total_accounts} account(s)...\n') + + for account in accounts: + self.stdout.write('=' * 60) + self.stdout.write(f'Account: {account.name} (ID: {account.id})') + self.stdout.write(f'Plan: {account.plan.name if account.plan else "No Plan"}') + self.stdout.write('=' * 60) + + # Count content ideas + ideas_count = ContentIdeas.objects.filter(account=account).count() + self.stdout.write(f'Content Ideas: {ideas_count}') + + # Count content words + from django.db.models import Sum + total_words = Content.objects.filter(account=account).aggregate( + total=Sum('word_count') + )['total'] or 0 + self.stdout.write(f'Content Words: {total_words}') + + # Count images + total_images = Images.objects.filter(account=account).count() + images_with_prompts = Images.objects.filter( + account=account, prompt__isnull=False + ).exclude(prompt='').count() + self.stdout.write(f'Total Images: {total_images}') + self.stdout.write(f'Images with Prompts: {images_with_prompts}') + + # Update account usage fields + with transaction.atomic(): + account.usage_content_ideas = ideas_count + account.usage_content_words = total_words + account.usage_images_basic = total_images + account.usage_images_premium = 0 # Premium not implemented yet + account.usage_image_prompts = images_with_prompts + account.save(update_fields=[ + 'usage_content_ideas', 'usage_content_words', + 'usage_images_basic', 'usage_images_premium', 'usage_image_prompts', + 'updated_at' + ]) + + self.stdout.write(self.style.SUCCESS('\nāœ… Updated usage tracking:')) + self.stdout.write(f' usage_content_ideas: {account.usage_content_ideas}') + self.stdout.write(f' usage_content_words: {account.usage_content_words}') + self.stdout.write(f' usage_images_basic: {account.usage_images_basic}') + self.stdout.write(f' usage_images_premium: {account.usage_images_premium}') + self.stdout.write(f' usage_image_prompts: {account.usage_image_prompts}\n') + + self.stdout.write('=' * 60) + self.stdout.write(self.style.SUCCESS('āœ… Backfill complete!')) + self.stdout.write('=' * 60) diff --git a/backend/igny8_core/business/content/models.py b/backend/igny8_core/business/content/models.py index 9388d071..0ec42922 100644 --- a/backend/igny8_core/business/content/models.py +++ b/backend/igny8_core/business/content/models.py @@ -459,8 +459,10 @@ class Images(SoftDeletableModel, SiteSectorBaseModel): all_objects = models.Manager() def save(self, *args, **kwargs): - """Automatically set account, site, and sector from content or task""" - # Prefer content over task + """Track image usage when creating new images""" + is_new = self.pk is None + + # Automatically set account, site, and sector from content or task if self.content: self.account = self.content.account self.site = self.content.site @@ -469,7 +471,44 @@ class Images(SoftDeletableModel, SiteSectorBaseModel): self.account = self.task.account self.site = self.task.site self.sector = self.task.sector + super().save(*args, **kwargs) + + # Increment usage for new images + if is_new: + from igny8_core.business.billing.services.limit_service import LimitService + try: + account = self.account + if account: + # Track image prompt usage + if self.prompt: + LimitService.increment_usage( + account=account, + limit_type='image_prompts', + amount=1, + metadata={ + 'image_id': self.id, + 'image_type': self.image_type, + 'content_id': self.content.id if self.content else None, + } + ) + + # Track basic image usage (for now, all images are counted as basic) + # TODO: Implement premium image tracking when premium models are used + LimitService.increment_usage( + account=account, + limit_type='images_basic', + amount=1, + metadata={ + 'image_id': self.id, + 'image_type': self.image_type, + 'content_id': self.content.id if self.content else None, + } + ) + except Exception as e: + import logging + logger = logging.getLogger(__name__) + logger.error(f"Error incrementing image usage for image {self.id}: {str(e)}") def __str__(self): content_title = self.content.title if self.content else None diff --git a/backend/igny8_core/business/planning/models.py b/backend/igny8_core/business/planning/models.py index 63eb6bf8..be6cb1d4 100644 --- a/backend/igny8_core/business/planning/models.py +++ b/backend/igny8_core/business/planning/models.py @@ -242,6 +242,32 @@ class ContentIdeas(SoftDeletableModel, SiteSectorBaseModel): objects = SoftDeleteManager() all_objects = models.Manager() + def save(self, *args, **kwargs): + """Track content ideas usage when creating new ideas""" + is_new = self.pk is None + super().save(*args, **kwargs) + + # Increment usage for new content ideas + if is_new: + from igny8_core.business.billing.services.limit_service import LimitService + try: + account = self.site.account if self.site else self.account + if account: + LimitService.increment_usage( + account=account, + limit_type='content_ideas', + amount=1, + metadata={ + 'idea_id': self.id, + 'idea_title': self.idea_title, + 'site_id': self.site.id if self.site else None, + } + ) + except Exception as e: + import logging + logger = logging.getLogger(__name__) + logger.error(f"Error incrementing content ideas usage for idea {self.id}: {str(e)}") + def __str__(self): return self.idea_title