fixes but still nto fixed
This commit is contained in:
@@ -757,20 +757,109 @@ class KeywordViewSet(SiteSectorModelViewSet):
|
||||
def filter_options(self, request):
|
||||
"""
|
||||
Get distinct filter values from current data.
|
||||
Returns only countries and statuses that exist in the current site's keywords.
|
||||
Returns only values that exist in the filtered queryset.
|
||||
Supports cascading filters - pass current filter values to get remaining options.
|
||||
"""
|
||||
import logging
|
||||
from django.db.models import Q
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Get distinct countries from seed_keyword (use set for proper deduplication)
|
||||
countries = list(set(queryset.values_list('seed_keyword__country', flat=True)))
|
||||
countries = sorted([c for c in countries if c]) # Sort and filter nulls
|
||||
# Apply current filters to get cascading options
|
||||
# Each filter's options are based on data that matches OTHER filters
|
||||
status_filter = request.query_params.get('status')
|
||||
country_filter = request.query_params.get('country')
|
||||
cluster_filter = request.query_params.get('cluster_id')
|
||||
difficulty_min = request.query_params.get('difficulty_min')
|
||||
difficulty_max = request.query_params.get('difficulty_max')
|
||||
|
||||
# Base queryset for each filter option calculation
|
||||
# For countries: apply status, cluster, difficulty filters
|
||||
countries_qs = queryset
|
||||
if status_filter:
|
||||
countries_qs = countries_qs.filter(status=status_filter)
|
||||
if cluster_filter:
|
||||
countries_qs = countries_qs.filter(cluster_id=cluster_filter)
|
||||
if difficulty_min is not None:
|
||||
try:
|
||||
countries_qs = countries_qs.filter(
|
||||
Q(difficulty_override__gte=int(difficulty_min)) |
|
||||
Q(difficulty_override__isnull=True, seed_keyword__difficulty__gte=int(difficulty_min))
|
||||
)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if difficulty_max is not None:
|
||||
try:
|
||||
countries_qs = countries_qs.filter(
|
||||
Q(difficulty_override__lte=int(difficulty_max)) |
|
||||
Q(difficulty_override__isnull=True, seed_keyword__difficulty__lte=int(difficulty_max))
|
||||
)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# For statuses: apply country, cluster, difficulty filters
|
||||
statuses_qs = queryset
|
||||
if country_filter:
|
||||
statuses_qs = statuses_qs.filter(seed_keyword__country=country_filter)
|
||||
if cluster_filter:
|
||||
statuses_qs = statuses_qs.filter(cluster_id=cluster_filter)
|
||||
if difficulty_min is not None:
|
||||
try:
|
||||
statuses_qs = statuses_qs.filter(
|
||||
Q(difficulty_override__gte=int(difficulty_min)) |
|
||||
Q(difficulty_override__isnull=True, seed_keyword__difficulty__gte=int(difficulty_min))
|
||||
)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if difficulty_max is not None:
|
||||
try:
|
||||
statuses_qs = statuses_qs.filter(
|
||||
Q(difficulty_override__lte=int(difficulty_max)) |
|
||||
Q(difficulty_override__isnull=True, seed_keyword__difficulty__lte=int(difficulty_max))
|
||||
)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# For clusters: apply status, country, difficulty filters
|
||||
clusters_qs = queryset
|
||||
if status_filter:
|
||||
clusters_qs = clusters_qs.filter(status=status_filter)
|
||||
if country_filter:
|
||||
clusters_qs = clusters_qs.filter(seed_keyword__country=country_filter)
|
||||
if difficulty_min is not None:
|
||||
try:
|
||||
clusters_qs = clusters_qs.filter(
|
||||
Q(difficulty_override__gte=int(difficulty_min)) |
|
||||
Q(difficulty_override__isnull=True, seed_keyword__difficulty__gte=int(difficulty_min))
|
||||
)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if difficulty_max is not None:
|
||||
try:
|
||||
clusters_qs = clusters_qs.filter(
|
||||
Q(difficulty_override__lte=int(difficulty_max)) |
|
||||
Q(difficulty_override__isnull=True, seed_keyword__difficulty__lte=int(difficulty_max))
|
||||
)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# For difficulties: apply status, country, cluster filters
|
||||
difficulties_qs = queryset
|
||||
if status_filter:
|
||||
difficulties_qs = difficulties_qs.filter(status=status_filter)
|
||||
if country_filter:
|
||||
difficulties_qs = difficulties_qs.filter(seed_keyword__country=country_filter)
|
||||
if cluster_filter:
|
||||
difficulties_qs = difficulties_qs.filter(cluster_id=cluster_filter)
|
||||
|
||||
# Get distinct countries
|
||||
countries = list(set(countries_qs.values_list('seed_keyword__country', flat=True)))
|
||||
countries = sorted([c for c in countries if c])
|
||||
|
||||
# Map country codes to display names
|
||||
from igny8_core.auth.models import SeedKeyword
|
||||
country_choices = dict(SeedKeyword.COUNTRY_CHOICES)
|
||||
country_options = [
|
||||
@@ -778,9 +867,9 @@ class KeywordViewSet(SiteSectorModelViewSet):
|
||||
for c in countries
|
||||
]
|
||||
|
||||
# Get distinct statuses (use set for proper deduplication)
|
||||
statuses = list(set(queryset.values_list('status', flat=True)))
|
||||
statuses = sorted([s for s in statuses if s]) # Sort and filter nulls
|
||||
# Get distinct statuses from filtered queryset
|
||||
statuses = list(set(statuses_qs.values_list('status', flat=True)))
|
||||
statuses = sorted([s for s in statuses if s])
|
||||
status_labels = {
|
||||
'new': 'New',
|
||||
'mapped': 'Mapped',
|
||||
@@ -790,9 +879,9 @@ class KeywordViewSet(SiteSectorModelViewSet):
|
||||
for s in statuses
|
||||
]
|
||||
|
||||
# Get distinct clusters (use set for proper deduplication)
|
||||
# Get distinct clusters from filtered queryset
|
||||
cluster_ids = list(set(
|
||||
queryset.exclude(cluster_id__isnull=True)
|
||||
clusters_qs.exclude(cluster_id__isnull=True)
|
||||
.values_list('cluster_id', flat=True)
|
||||
))
|
||||
|
||||
@@ -802,11 +891,48 @@ class KeywordViewSet(SiteSectorModelViewSet):
|
||||
for c in clusters
|
||||
]
|
||||
|
||||
# Get distinct difficulty levels from filtered queryset (mapped to 1-5 scale)
|
||||
# Difficulty is stored as 0-100 in seed_keyword, mapped to 1-5 scale
|
||||
from django.db.models import Case, When, Value, IntegerField
|
||||
|
||||
# Get effective difficulty (override or seed_keyword)
|
||||
difficulty_values = difficulties_qs.annotate(
|
||||
effective_difficulty=Coalesce('difficulty_override', 'seed_keyword__difficulty')
|
||||
).exclude(effective_difficulty__isnull=True).values_list('effective_difficulty', flat=True)
|
||||
|
||||
# Map raw difficulty (0-100) to 1-5 scale and find unique values
|
||||
difficulty_levels = set()
|
||||
for d in difficulty_values:
|
||||
if d is not None:
|
||||
if d <= 10:
|
||||
difficulty_levels.add(1)
|
||||
elif d <= 30:
|
||||
difficulty_levels.add(2)
|
||||
elif d <= 50:
|
||||
difficulty_levels.add(3)
|
||||
elif d <= 70:
|
||||
difficulty_levels.add(4)
|
||||
else:
|
||||
difficulty_levels.add(5)
|
||||
|
||||
difficulty_labels = {
|
||||
1: '1 - Very Easy',
|
||||
2: '2 - Easy',
|
||||
3: '3 - Medium',
|
||||
4: '4 - Hard',
|
||||
5: '5 - Very Hard',
|
||||
}
|
||||
difficulty_options = [
|
||||
{'value': str(d), 'label': difficulty_labels[d]}
|
||||
for d in sorted(difficulty_levels)
|
||||
]
|
||||
|
||||
return success_response(
|
||||
data={
|
||||
'countries': country_options,
|
||||
'statuses': status_options,
|
||||
'clusters': cluster_options,
|
||||
'difficulties': difficulty_options,
|
||||
},
|
||||
request=request
|
||||
)
|
||||
@@ -1130,6 +1256,45 @@ class ClusterViewSet(SiteSectorModelViewSet):
|
||||
|
||||
return success_response(data={'deleted_count': deleted_count}, request=request)
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='filter_options', url_name='filter_options')
|
||||
def filter_options(self, request):
|
||||
"""
|
||||
Get distinct filter values from current data.
|
||||
Returns only statuses that exist in the current site's clusters.
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Get distinct statuses
|
||||
statuses = list(set(queryset.values_list('status', flat=True)))
|
||||
statuses = sorted([s for s in statuses if s])
|
||||
status_labels = {
|
||||
'new': 'New',
|
||||
'mapped': 'Mapped',
|
||||
}
|
||||
status_options = [
|
||||
{'value': s, 'label': status_labels.get(s, s.title())}
|
||||
for s in statuses
|
||||
]
|
||||
|
||||
return success_response(
|
||||
data={
|
||||
'statuses': status_options,
|
||||
},
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in filter_options: {str(e)}", exc_info=True)
|
||||
return error_response(
|
||||
error=f'Failed to fetch filter options: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='auto_generate_ideas', url_name='auto_generate_ideas')
|
||||
def auto_generate_ideas(self, request):
|
||||
"""Auto-generate ideas for clusters using IdeasService"""
|
||||
@@ -1308,6 +1473,90 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
|
||||
|
||||
serializer.save(account=account, site=site, sector=sector)
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='filter_options', url_name='filter_options')
|
||||
def filter_options(self, request):
|
||||
"""
|
||||
Get distinct filter values from current data.
|
||||
Returns only values that exist in the current site's content ideas.
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Get distinct statuses
|
||||
statuses = list(set(queryset.values_list('status', flat=True)))
|
||||
statuses = sorted([s for s in statuses if s])
|
||||
status_labels = {
|
||||
'new': 'New',
|
||||
'queued': 'Queued',
|
||||
'completed': 'Completed',
|
||||
}
|
||||
status_options = [
|
||||
{'value': s, 'label': status_labels.get(s, s.title())}
|
||||
for s in statuses
|
||||
]
|
||||
|
||||
# Get distinct content_types
|
||||
content_types = list(set(queryset.values_list('content_type', flat=True)))
|
||||
content_types = sorted([t for t in content_types if t])
|
||||
type_labels = {
|
||||
'post': 'Post',
|
||||
'page': 'Page',
|
||||
'product': 'Product',
|
||||
'taxonomy': 'Taxonomy',
|
||||
}
|
||||
content_type_options = [
|
||||
{'value': t, 'label': type_labels.get(t, t.title())}
|
||||
for t in content_types
|
||||
]
|
||||
|
||||
# Get distinct content_structures
|
||||
structures = list(set(queryset.values_list('content_structure', flat=True)))
|
||||
structures = sorted([s for s in structures if s])
|
||||
structure_labels = {
|
||||
'article': 'Article', 'guide': 'Guide', 'comparison': 'Comparison',
|
||||
'review': 'Review', 'listicle': 'Listicle', 'landing_page': 'Landing Page',
|
||||
'business_page': 'Business Page', 'service_page': 'Service Page',
|
||||
'general': 'General', 'cluster_hub': 'Cluster Hub',
|
||||
'product_page': 'Product Page', 'category_archive': 'Category Archive',
|
||||
'tag_archive': 'Tag Archive', 'attribute_archive': 'Attribute Archive',
|
||||
}
|
||||
content_structure_options = [
|
||||
{'value': s, 'label': structure_labels.get(s, s.replace('_', ' ').title())}
|
||||
for s in structures
|
||||
]
|
||||
|
||||
# Get distinct clusters with ideas
|
||||
cluster_ids = list(set(
|
||||
queryset.exclude(keyword_cluster_id__isnull=True)
|
||||
.values_list('keyword_cluster_id', flat=True)
|
||||
))
|
||||
clusters = Clusters.objects.filter(id__in=cluster_ids).values('id', 'name').order_by('name')
|
||||
cluster_options = [
|
||||
{'value': str(c['id']), 'label': c['name']}
|
||||
for c in clusters
|
||||
]
|
||||
|
||||
return success_response(
|
||||
data={
|
||||
'statuses': status_options,
|
||||
'content_types': content_type_options,
|
||||
'content_structures': content_structure_options,
|
||||
'clusters': cluster_options,
|
||||
},
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in filter_options: {str(e)}", exc_info=True)
|
||||
return error_response(
|
||||
error=f'Failed to fetch filter options: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['POST'], url_path='bulk_delete', url_name='bulk_delete')
|
||||
def bulk_delete(self, request):
|
||||
"""Bulk delete content ideas"""
|
||||
|
||||
@@ -824,6 +824,108 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
else:
|
||||
serializer.save()
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='filter_options', url_name='filter_options')
|
||||
def filter_options(self, request):
|
||||
"""
|
||||
Get distinct filter values from current data.
|
||||
Returns only values that exist in the current site's content.
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Get distinct statuses
|
||||
statuses = list(set(queryset.values_list('status', flat=True)))
|
||||
statuses = sorted([s for s in statuses if s])
|
||||
status_labels = {
|
||||
'draft': 'Draft',
|
||||
'review': 'Review',
|
||||
'approved': 'Approved',
|
||||
'published': 'Published',
|
||||
}
|
||||
status_options = [
|
||||
{'value': s, 'label': status_labels.get(s, s.title())}
|
||||
for s in statuses
|
||||
]
|
||||
|
||||
# Get distinct site_status
|
||||
site_statuses = list(set(queryset.values_list('site_status', flat=True)))
|
||||
site_statuses = sorted([s for s in site_statuses if s])
|
||||
site_status_labels = {
|
||||
'not_published': 'Not Published',
|
||||
'scheduled': 'Scheduled',
|
||||
'publishing': 'Publishing',
|
||||
'published': 'Published',
|
||||
'failed': 'Failed',
|
||||
}
|
||||
site_status_options = [
|
||||
{'value': s, 'label': site_status_labels.get(s, s.replace('_', ' ').title())}
|
||||
for s in site_statuses
|
||||
]
|
||||
|
||||
# Get distinct content_types
|
||||
content_types = list(set(queryset.values_list('content_type', flat=True)))
|
||||
content_types = sorted([t for t in content_types if t])
|
||||
type_labels = {
|
||||
'post': 'Post',
|
||||
'page': 'Page',
|
||||
'product': 'Product',
|
||||
'taxonomy': 'Taxonomy',
|
||||
}
|
||||
content_type_options = [
|
||||
{'value': t, 'label': type_labels.get(t, t.title())}
|
||||
for t in content_types
|
||||
]
|
||||
|
||||
# Get distinct content_structures
|
||||
structures = list(set(queryset.values_list('content_structure', flat=True)))
|
||||
structures = sorted([s for s in structures if s])
|
||||
structure_labels = {
|
||||
'article': 'Article', 'guide': 'Guide', 'comparison': 'Comparison',
|
||||
'review': 'Review', 'listicle': 'Listicle', 'landing_page': 'Landing Page',
|
||||
'business_page': 'Business Page', 'service_page': 'Service Page',
|
||||
'general': 'General', 'cluster_hub': 'Cluster Hub',
|
||||
'product_page': 'Product Page', 'category_archive': 'Category Archive',
|
||||
'tag_archive': 'Tag Archive', 'attribute_archive': 'Attribute Archive',
|
||||
}
|
||||
content_structure_options = [
|
||||
{'value': s, 'label': structure_labels.get(s, s.replace('_', ' ').title())}
|
||||
for s in structures
|
||||
]
|
||||
|
||||
# Get distinct sources
|
||||
sources = list(set(queryset.values_list('source', flat=True)))
|
||||
sources = sorted([s for s in sources if s])
|
||||
source_labels = {
|
||||
'igny8': 'IGNY8',
|
||||
'wordpress': 'WordPress',
|
||||
}
|
||||
source_options = [
|
||||
{'value': s, 'label': source_labels.get(s, s.title())}
|
||||
for s in sources
|
||||
]
|
||||
|
||||
return success_response(
|
||||
data={
|
||||
'statuses': status_options,
|
||||
'site_statuses': site_status_options,
|
||||
'content_types': content_type_options,
|
||||
'content_structures': content_structure_options,
|
||||
'sources': source_options,
|
||||
},
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in filter_options: {str(e)}", exc_info=True)
|
||||
return error_response(
|
||||
error=f'Failed to fetch filter options: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['POST'], url_path='bulk_delete', url_name='bulk_delete')
|
||||
def bulk_delete(self, request):
|
||||
"""Bulk delete content"""
|
||||
|
||||
Reference in New Issue
Block a user