Refactor keyword handling: Replace 'intent' with 'country' across backend and frontend

- Updated AutomationService to include estimated_word_count.
- Increased stage_1_batch_size from 20 to 50 in AutomationViewSet.
- Changed Keywords model to replace 'intent' property with 'country'.
- Adjusted ClusteringService to allow a maximum of 50 keywords for clustering.
- Modified admin and management commands to remove 'intent' and use 'country' instead.
- Updated serializers to reflect the change from 'intent' to 'country'.
- Adjusted views and filters to use 'country' instead of 'intent'.
- Updated frontend forms, filters, and pages to replace 'intent' with 'country'.
- Added migration to remove 'intent' field and add 'country' field to SeedKeyword model.
This commit is contained in:
IGNY8 VPS (Salman)
2025-12-17 07:37:36 +00:00
parent 9f826c92f8
commit 7ad06c6227
30 changed files with 240 additions and 205 deletions

View File

@@ -18,7 +18,7 @@ class KeywordsResource(resources.ModelResource):
class Meta:
model = Keywords
fields = ('id', 'keyword', 'seed_keyword__keyword', 'site__name', 'sector__name',
'cluster__name', 'volume', 'difficulty', 'intent', 'status', 'created_at')
'cluster__name', 'volume', 'difficulty', 'country', 'status', 'created_at')
export_order = fields
@@ -55,11 +55,11 @@ class ClustersAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
@admin.register(Keywords)
class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
resource_class = KeywordsResource
list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'intent', 'status', 'created_at']
list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'country', 'status', 'created_at']
list_editable = ['status'] # Enable inline editing for status
list_filter = [
('status', ChoicesDropdownFilter),
('intent', ChoicesDropdownFilter),
('country', ChoicesDropdownFilter),
('site', RelatedDropdownFilter),
('sector', RelatedDropdownFilter),
('cluster', RelatedDropdownFilter),

View File

@@ -109,7 +109,6 @@ class Command(BaseCommand):
'account': site.account,
'volume': 1000 + (created_count * 100), # Varying volumes
'difficulty': 30 + (created_count * 10), # Varying difficulty (0-100 scale)
'intent': 'informational' if created_count % 2 == 0 else 'commercial',
'status': 'active',
}
)

View File

@@ -243,7 +243,6 @@ class Command(BaseCommand):
'account': site.account,
'volume': 500 + (created_count * 50), # Varying volumes
'difficulty': 20 + (created_count * 8), # Varying difficulty (0-100 scale)
'intent': 'informational' if created_count % 2 == 0 else 'commercial',
'status': 'active',
}
)

View File

@@ -93,7 +93,7 @@ class Command(BaseCommand):
sector=industry_sector,
volume=keyword.volume or 0,
difficulty=keyword.difficulty or 0,
intent=keyword.intent or 'informational',
country='US', # Default country for migration
is_active=True
)
created_count += 1

View File

@@ -13,7 +13,7 @@ class KeywordSerializer(serializers.ModelSerializer):
keyword = serializers.CharField(read_only=True) # From seed_keyword.keyword
volume = serializers.IntegerField(read_only=True) # From seed_keyword.volume or volume_override
difficulty = serializers.IntegerField(read_only=True) # From seed_keyword.difficulty or difficulty_override
intent = serializers.CharField(read_only=True) # From seed_keyword.intent
country = serializers.CharField(read_only=True) # From seed_keyword.country
# SeedKeyword relationship
# Optional for create - can either provide seed_keyword_id OR custom keyword fields
@@ -24,11 +24,11 @@ class KeywordSerializer(serializers.ModelSerializer):
custom_keyword = serializers.CharField(write_only=True, required=False, allow_blank=False)
custom_volume = serializers.IntegerField(write_only=True, required=False, allow_null=True)
custom_difficulty = serializers.IntegerField(write_only=True, required=False, allow_null=True)
custom_intent = serializers.ChoiceField(
custom_country = serializers.ChoiceField(
write_only=True,
required=False,
choices=['informational', 'navigational', 'transactional', 'commercial'],
default='informational'
choices=['US', 'CA', 'GB', 'AE', 'AU', 'IN', 'PK'],
default='US'
)
# Overrides
@@ -50,11 +50,11 @@ class KeywordSerializer(serializers.ModelSerializer):
'keyword',
'volume',
'difficulty',
'intent',
'country',
'custom_keyword', # Write-only field for creating custom keywords
'custom_volume', # Write-only
'custom_difficulty', # Write-only
'custom_intent', # Write-only
'custom_country', # Write-only
'volume_override',
'difficulty_override',
'cluster_id',
@@ -67,7 +67,7 @@ class KeywordSerializer(serializers.ModelSerializer):
'sector_id',
'account_id',
]
read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keyword', 'volume', 'difficulty', 'intent']
read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keyword', 'volume', 'difficulty', 'country']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -106,7 +106,7 @@ class KeywordSerializer(serializers.ModelSerializer):
custom_keyword = validated_data.pop('custom_keyword', None)
custom_volume = validated_data.pop('custom_volume', None)
custom_difficulty = validated_data.pop('custom_difficulty', None)
custom_intent = validated_data.pop('custom_intent', None) or 'informational'
custom_country = validated_data.pop('custom_country', None) or 'US'
# Get site and sector - they're passed as objects via save() in the view
site = validated_data.get('site')
@@ -132,7 +132,7 @@ class KeywordSerializer(serializers.ModelSerializer):
defaults={
'volume': custom_volume or 0,
'difficulty': custom_difficulty or 0,
'intent': custom_intent or 'informational',
'country': custom_country or 'US',
'is_active': True,
}
)
@@ -162,7 +162,7 @@ class KeywordSerializer(serializers.ModelSerializer):
validated_data.pop('custom_keyword', None)
validated_data.pop('custom_volume', None)
validated_data.pop('custom_difficulty', None)
validated_data.pop('custom_intent', None)
validated_data.pop('custom_country', None)
# seed_keyword_id is optional for updates - only update if provided
if 'seed_keyword_id' in validated_data:

View File

@@ -55,7 +55,7 @@ class KeywordViewSet(SiteSectorModelViewSet):
ordering = ['-created_at'] # Default ordering (newest first)
# Filter configuration - filter by status, cluster_id, and seed_keyword fields
filterset_fields = ['status', 'cluster_id', 'seed_keyword__intent', 'seed_keyword_id']
filterset_fields = ['status', 'cluster_id', 'seed_keyword__country', 'seed_keyword_id']
def get_queryset(self):
"""
@@ -475,7 +475,7 @@ class KeywordViewSet(SiteSectorModelViewSet):
writer = csv.writer(response)
# Header row
writer.writerow(['ID', 'Keyword', 'Volume', 'Difficulty', 'Intent', 'Status', 'Cluster ID', 'Created At'])
writer.writerow(['ID', 'Keyword', 'Volume', 'Difficulty', 'Country', 'Status', 'Cluster ID', 'Created At'])
# Data rows
for keyword in keywords:
@@ -484,7 +484,7 @@ class KeywordViewSet(SiteSectorModelViewSet):
keyword.keyword,
keyword.volume,
keyword.difficulty,
keyword.intent,
keyword.country,
keyword.status,
keyword.cluster_id or '',
keyword.created_at.isoformat() if keyword.created_at else '',
@@ -631,12 +631,13 @@ class KeywordViewSet(SiteSectorModelViewSet):
skipped_count += 1
continue
# Create keyword
# Note: This direct creation bypasses seed_keyword linkage
# Keywords should ideally be created through seed_keyword FK
# Country comes from seed_keyword.country property
Keywords.objects.create(
keyword=keyword_text,
volume=int(row.get('volume', 0) or 0),
difficulty=int(row.get('difficulty', 0) or 0),
intent=row.get('intent', 'informational') or 'informational',
status=row.get('status', 'new') or 'new',
site=site,
sector=sector,
@@ -1193,6 +1194,7 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
content_structure=idea.content_structure or 'article',
taxonomy_term=None, # Can be set later if taxonomy is available
keywords=keywords_str, # Comma-separated keywords string
word_count=idea.estimated_word_count, # Copy word count from idea
status='queued',
account=idea.account,
site=idea.site,