keywrods library fixes
This commit is contained in:
@@ -869,6 +869,8 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
difficulty_max = self.request.query_params.get('difficulty_max')
|
||||
volume_min = self.request.query_params.get('volume_min')
|
||||
volume_max = self.request.query_params.get('volume_max')
|
||||
site_id = self.request.query_params.get('site_id')
|
||||
available_only = self.request.query_params.get('available_only')
|
||||
|
||||
if industry_id:
|
||||
queryset = queryset.filter(industry_id=industry_id)
|
||||
@@ -902,6 +904,19 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = queryset.filter(volume__lte=int(volume_max))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Availability filter - exclude keywords already added to the site
|
||||
if available_only and str(available_only).lower() in ['true', '1', 'yes']:
|
||||
if site_id:
|
||||
try:
|
||||
from igny8_core.business.planning.models import Keywords
|
||||
attached_ids = Keywords.objects.filter(
|
||||
site_id=site_id,
|
||||
seed_keyword__isnull=False
|
||||
).values_list('seed_keyword_id', flat=True)
|
||||
queryset = queryset.exclude(id__in=attached_ids)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return queryset
|
||||
|
||||
@@ -1152,6 +1167,9 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
# 3. High Volume (>= 10K) - simple threshold
|
||||
high_volume_count = base_qs.filter(volume__gte=10000).count()
|
||||
|
||||
# 3b. Mid Volume (5K-10K)
|
||||
mid_volume_count = base_qs.filter(volume__gte=5000, volume__lt=10000).count()
|
||||
|
||||
# 4. Premium Traffic with dynamic fallback (50K -> 25K -> 10K)
|
||||
premium_thresholds = [50000, 25000, 10000]
|
||||
@@ -1182,6 +1200,7 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
'total': {'count': total_count},
|
||||
'available': {'count': available_count},
|
||||
'high_volume': {'count': high_volume_count, 'threshold': 10000},
|
||||
'mid_volume': {'count': mid_volume_count, 'threshold': 5000},
|
||||
'premium_traffic': premium_result,
|
||||
'long_tail': long_tail_result,
|
||||
'quick_wins': quick_wins_result,
|
||||
@@ -1201,6 +1220,7 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
sector_available = count_available(sector_qs)
|
||||
sector_high_volume = sector_qs.filter(volume__gte=10000).count()
|
||||
sector_mid_volume = sector_qs.filter(volume__gte=5000, volume__lt=10000).count()
|
||||
sector_premium = get_count_with_fallback(sector_qs, premium_thresholds)
|
||||
|
||||
sector_long_tail_base = sector_qs.filter(keyword__regex=r'^(\S+\s+){3,}\S+$')
|
||||
@@ -1218,6 +1238,7 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
'total': {'count': sector_total},
|
||||
'available': {'count': sector_available},
|
||||
'high_volume': {'count': sector_high_volume, 'threshold': 10000},
|
||||
'mid_volume': {'count': sector_mid_volume, 'threshold': 5000},
|
||||
'premium_traffic': sector_premium,
|
||||
'long_tail': sector_long_tail,
|
||||
'quick_wins': sector_quick_wins,
|
||||
@@ -1243,12 +1264,20 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
Get cascading filter options for Keywords Library.
|
||||
Returns industries, sectors (filtered by industry), and available filter values.
|
||||
Supports cascading options based on current filters.
|
||||
"""
|
||||
from django.db.models import Count, Min, Max, Q
|
||||
|
||||
try:
|
||||
industry_id = request.query_params.get('industry_id')
|
||||
|
||||
sector_id = request.query_params.get('sector_id')
|
||||
country_filter = request.query_params.get('country')
|
||||
difficulty_min = request.query_params.get('difficulty_min')
|
||||
difficulty_max = request.query_params.get('difficulty_max')
|
||||
volume_min = request.query_params.get('volume_min')
|
||||
volume_max = request.query_params.get('volume_max')
|
||||
search_term = request.query_params.get('search')
|
||||
|
||||
# Get industries with keyword counts
|
||||
industries = Industry.objects.annotate(
|
||||
keyword_count=Count('seed_keywords', filter=Q(seed_keywords__is_active=True))
|
||||
@@ -1276,31 +1305,120 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
'slug': sec.slug,
|
||||
'keyword_count': sec.keyword_count,
|
||||
} for sec in sectors]
|
||||
|
||||
# Get difficulty range
|
||||
difficulty_range = SeedKeyword.objects.filter(is_active=True).aggregate(
|
||||
|
||||
# Base queryset for cascading options
|
||||
base_qs = SeedKeyword.objects.filter(is_active=True)
|
||||
if industry_id:
|
||||
base_qs = base_qs.filter(industry_id=industry_id)
|
||||
if sector_id:
|
||||
base_qs = base_qs.filter(sector_id=sector_id)
|
||||
|
||||
# Countries options - apply all filters except country itself
|
||||
countries_qs = base_qs
|
||||
if difficulty_min is not None:
|
||||
try:
|
||||
countries_qs = countries_qs.filter(difficulty__gte=int(difficulty_min))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if difficulty_max is not None:
|
||||
try:
|
||||
countries_qs = countries_qs.filter(difficulty__lte=int(difficulty_max))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if volume_min is not None:
|
||||
try:
|
||||
countries_qs = countries_qs.filter(volume__gte=int(volume_min))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if volume_max is not None:
|
||||
try:
|
||||
countries_qs = countries_qs.filter(volume__lte=int(volume_max))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if search_term:
|
||||
countries_qs = countries_qs.filter(keyword__icontains=search_term)
|
||||
|
||||
countries = countries_qs.values('country').annotate(
|
||||
keyword_count=Count('id')
|
||||
).order_by('country')
|
||||
country_label_map = dict(SeedKeyword.COUNTRY_CHOICES)
|
||||
countries_data = [{
|
||||
'value': c['country'],
|
||||
'label': country_label_map.get(c['country'], c['country']),
|
||||
'keyword_count': c['keyword_count'],
|
||||
} for c in countries if c['country']]
|
||||
|
||||
# Difficulty options - apply all filters except difficulty itself
|
||||
difficulty_qs = base_qs
|
||||
if country_filter:
|
||||
difficulty_qs = difficulty_qs.filter(country=country_filter)
|
||||
if volume_min is not None:
|
||||
try:
|
||||
difficulty_qs = difficulty_qs.filter(volume__gte=int(volume_min))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if volume_max is not None:
|
||||
try:
|
||||
difficulty_qs = difficulty_qs.filter(volume__lte=int(volume_max))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if search_term:
|
||||
difficulty_qs = difficulty_qs.filter(keyword__icontains=search_term)
|
||||
|
||||
difficulty_ranges = [
|
||||
(1, 'Very Easy', 0, 10),
|
||||
(2, 'Easy', 11, 30),
|
||||
(3, 'Medium', 31, 50),
|
||||
(4, 'Hard', 51, 70),
|
||||
(5, 'Very Hard', 71, 100),
|
||||
]
|
||||
|
||||
difficulty_levels = []
|
||||
for level, label, min_val, max_val in difficulty_ranges:
|
||||
count = difficulty_qs.filter(
|
||||
difficulty__gte=min_val,
|
||||
difficulty__lte=max_val
|
||||
).count()
|
||||
if count > 0:
|
||||
difficulty_levels.append({
|
||||
'level': level,
|
||||
'label': label,
|
||||
'backend_range': [min_val, max_val],
|
||||
'keyword_count': count,
|
||||
})
|
||||
|
||||
# Difficulty range (filtered by current non-difficulty filters)
|
||||
difficulty_range = difficulty_qs.aggregate(
|
||||
min_difficulty=Min('difficulty'),
|
||||
max_difficulty=Max('difficulty')
|
||||
)
|
||||
|
||||
# Get volume range
|
||||
volume_range = SeedKeyword.objects.filter(is_active=True).aggregate(
|
||||
|
||||
# Volume range (filtered by current non-volume filters)
|
||||
volume_qs = base_qs
|
||||
if country_filter:
|
||||
volume_qs = volume_qs.filter(country=country_filter)
|
||||
if difficulty_min is not None:
|
||||
try:
|
||||
volume_qs = volume_qs.filter(difficulty__gte=int(difficulty_min))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if difficulty_max is not None:
|
||||
try:
|
||||
volume_qs = volume_qs.filter(difficulty__lte=int(difficulty_max))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if search_term:
|
||||
volume_qs = volume_qs.filter(keyword__icontains=search_term)
|
||||
|
||||
volume_range = volume_qs.aggregate(
|
||||
min_volume=Min('volume'),
|
||||
max_volume=Max('volume')
|
||||
)
|
||||
|
||||
# Difficulty levels for frontend (maps to backend values)
|
||||
difficulty_levels = [
|
||||
{'level': 1, 'label': 'Very Easy', 'backend_range': [0, 20]},
|
||||
{'level': 2, 'label': 'Easy', 'backend_range': [21, 40]},
|
||||
{'level': 3, 'label': 'Medium', 'backend_range': [41, 60]},
|
||||
{'level': 4, 'label': 'Hard', 'backend_range': [61, 80]},
|
||||
{'level': 5, 'label': 'Very Hard', 'backend_range': [81, 100]},
|
||||
]
|
||||
|
||||
data = {
|
||||
'industries': industries_data,
|
||||
'sectors': sectors_data,
|
||||
'countries': countries_data,
|
||||
'difficulty': {
|
||||
'range': difficulty_range,
|
||||
'levels': difficulty_levels,
|
||||
|
||||
Reference in New Issue
Block a user