be fe fixes
This commit is contained in:
@@ -207,7 +207,8 @@ class GenerateImagePromptsFunction(BaseAIFunction):
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
|
||||
# Extract title
|
||||
title = content.title or content.task.title or ''
|
||||
# Get content title (task field was removed in refactor)
|
||||
title = content.title or ''
|
||||
|
||||
# Extract first 1-2 intro paragraphs (skip italic hook if present)
|
||||
paragraphs = soup.find_all('p')
|
||||
|
||||
@@ -22,15 +22,30 @@ class Tasks(SiteSectorBaseModel):
|
||||
limit_choices_to={'sector': models.F('sector')},
|
||||
help_text="Parent cluster (required)"
|
||||
)
|
||||
idea = models.ForeignKey(
|
||||
'planner.ContentIdeas',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='tasks',
|
||||
help_text="Optional content idea reference",
|
||||
db_column='idea_id'
|
||||
)
|
||||
content_type = models.CharField(
|
||||
max_length=100,
|
||||
db_index=True,
|
||||
help_text="Content type: post, page, product, service, category, tag, etc."
|
||||
help_text="Content type: post, page, product, service, category, tag, etc.",
|
||||
db_column='entity_type',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
content_structure = models.CharField(
|
||||
max_length=100,
|
||||
db_index=True,
|
||||
help_text="Content structure/format: article, listicle, guide, comparison, product_page, etc."
|
||||
help_text="Content structure/format: article, listicle, guide, comparison, product_page, etc.",
|
||||
db_column='cluster_role',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
taxonomy_term = models.ForeignKey(
|
||||
'ContentTaxonomy',
|
||||
@@ -38,13 +53,13 @@ class Tasks(SiteSectorBaseModel):
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='tasks',
|
||||
help_text="Optional taxonomy term assignment"
|
||||
help_text="Optional taxonomy term assignment",
|
||||
db_column='taxonomy_id'
|
||||
)
|
||||
keywords = models.ManyToManyField(
|
||||
'planner.Keywords',
|
||||
keywords = models.TextField(
|
||||
blank=True,
|
||||
related_name='tasks',
|
||||
help_text="Keywords linked to this task"
|
||||
null=True,
|
||||
help_text="Comma-separated keywords for this task"
|
||||
)
|
||||
status = models.CharField(max_length=50, choices=STATUS_CHOICES, default='queued')
|
||||
|
||||
@@ -70,6 +85,19 @@ class Tasks(SiteSectorBaseModel):
|
||||
return self.title
|
||||
|
||||
|
||||
class ContentTaxonomyRelation(models.Model):
|
||||
"""Through model for Content-Taxonomy many-to-many relationship"""
|
||||
content = models.ForeignKey('Content', on_delete=models.CASCADE, db_column='content_id')
|
||||
taxonomy = models.ForeignKey('ContentTaxonomy', on_delete=models.CASCADE, db_column='taxonomy_id')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
app_label = 'writer'
|
||||
db_table = 'igny8_content_taxonomy_relations'
|
||||
unique_together = [['content', 'taxonomy']]
|
||||
|
||||
|
||||
class Content(SiteSectorBaseModel):
|
||||
"""
|
||||
Content model for AI-generated or WordPress-imported content.
|
||||
@@ -78,7 +106,7 @@ class Content(SiteSectorBaseModel):
|
||||
|
||||
# Core content fields
|
||||
title = models.CharField(max_length=255, db_index=True)
|
||||
content_html = models.TextField(help_text="Final HTML content")
|
||||
content_html = models.TextField(help_text="Final HTML content", db_column='html_content')
|
||||
cluster = models.ForeignKey(
|
||||
'planner.Clusters',
|
||||
on_delete=models.SET_NULL,
|
||||
@@ -90,26 +118,34 @@ class Content(SiteSectorBaseModel):
|
||||
content_type = models.CharField(
|
||||
max_length=100,
|
||||
db_index=True,
|
||||
help_text="Content type: post, page, product, service, category, tag, etc."
|
||||
help_text="Content type: post, page, product, service, category, tag, etc.",
|
||||
db_column='entity_type',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
content_structure = models.CharField(
|
||||
max_length=100,
|
||||
db_index=True,
|
||||
help_text="Content structure/format: article, listicle, guide, comparison, product_page, etc."
|
||||
help_text="Content structure/format: article, listicle, guide, comparison, product_page, etc.",
|
||||
db_column='cluster_role',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
# Taxonomy relationships
|
||||
taxonomy_terms = models.ManyToManyField(
|
||||
'ContentTaxonomy',
|
||||
through='ContentTaxonomyRelation',
|
||||
blank=True,
|
||||
related_name='contents',
|
||||
db_table='igny8_content_taxonomy_relations',
|
||||
help_text="Associated taxonomy terms (categories, tags, attributes)"
|
||||
)
|
||||
|
||||
# External platform fields (WordPress integration)
|
||||
external_id = models.CharField(max_length=255, blank=True, null=True, db_index=True, help_text="WordPress/external platform post ID")
|
||||
external_url = models.URLField(blank=True, null=True, help_text="WordPress/external platform URL")
|
||||
external_type = models.CharField(max_length=100, blank=True, null=True, help_text="WordPress post type (post, page, product, etc.)")
|
||||
sync_status = models.CharField(max_length=50, blank=True, null=True, help_text="Sync status with WordPress")
|
||||
|
||||
# Source tracking
|
||||
SOURCE_CHOICES = [
|
||||
|
||||
@@ -23,62 +23,21 @@ class MetadataMappingService:
|
||||
@transaction.atomic
|
||||
def persist_task_metadata_to_content(self, content: Content) -> None:
|
||||
"""
|
||||
Persist cluster/taxonomy/attribute mappings from Task to Content.
|
||||
DEPRECATED: This method is deprecated as Content model no longer has task field.
|
||||
Metadata is now persisted directly on content model.
|
||||
|
||||
Args:
|
||||
content: Content instance with an associated task
|
||||
content: Content instance
|
||||
"""
|
||||
if not content.task:
|
||||
logger.warning(f"Content {content.id} has no associated task, skipping metadata mapping")
|
||||
return
|
||||
|
||||
task = content.task
|
||||
|
||||
# Stage 3: Persist cluster mapping if task has cluster
|
||||
if task.cluster:
|
||||
ContentClusterMap.objects.get_or_create(
|
||||
content=content,
|
||||
cluster=task.cluster,
|
||||
role=task.cluster_role or 'hub',
|
||||
defaults={
|
||||
'account': content.account,
|
||||
'site': content.site,
|
||||
'sector': content.sector,
|
||||
'source': 'blueprint' if task.idea else 'manual',
|
||||
'metadata': {},
|
||||
}
|
||||
)
|
||||
logger.info(f"Created cluster mapping for content {content.id} -> cluster {task.cluster.id}")
|
||||
|
||||
# Stage 3: Persist taxonomy mapping if task has taxonomy
|
||||
if task.taxonomy:
|
||||
ContentTaxonomyMap.objects.get_or_create(
|
||||
content=content,
|
||||
taxonomy=task.taxonomy,
|
||||
defaults={
|
||||
'account': content.account,
|
||||
'site': content.site,
|
||||
'sector': content.sector,
|
||||
'source': 'blueprint',
|
||||
'metadata': {},
|
||||
}
|
||||
)
|
||||
logger.info(f"Created taxonomy mapping for content {content.id} -> taxonomy {task.taxonomy.id}")
|
||||
|
||||
# Stage 3: Inherit entity_type from task
|
||||
if task.entity_type and not content.entity_type:
|
||||
content.entity_type = task.entity_type
|
||||
content.save(update_fields=['entity_type'])
|
||||
logger.info(f"Set entity_type {task.entity_type} for content {content.id}")
|
||||
|
||||
# Stage 3: Extract attributes from task metadata if available
|
||||
# This can be extended to parse task.description or task.metadata for attributes
|
||||
# For now, we'll rely on explicit attribute data in future enhancements
|
||||
logger.warning(f"[persist_task_metadata_to_content] Deprecated method called for content {content.id}")
|
||||
logger.warning(f"Content model no longer has task field - metadata should be set directly on content")
|
||||
return
|
||||
|
||||
@transaction.atomic
|
||||
def backfill_content_metadata(self, content: Content) -> None:
|
||||
"""
|
||||
Backfill metadata mappings for existing content that may be missing mappings.
|
||||
Note: task field was removed from Content model - only infers from content metadata.
|
||||
|
||||
Args:
|
||||
content: Content instance to backfill
|
||||
@@ -87,13 +46,24 @@ class MetadataMappingService:
|
||||
if ContentClusterMap.objects.filter(content=content).exists():
|
||||
return
|
||||
|
||||
# Try to infer from task
|
||||
if content.task:
|
||||
self.persist_task_metadata_to_content(content)
|
||||
# Try to infer from content metadata or cluster field
|
||||
if hasattr(content, 'cluster') and content.cluster:
|
||||
ContentClusterMap.objects.get_or_create(
|
||||
content=content,
|
||||
cluster=content.cluster,
|
||||
role='hub', # Default
|
||||
defaults={
|
||||
'account': content.account,
|
||||
'site': content.site,
|
||||
'sector': content.sector,
|
||||
'source': 'manual',
|
||||
'metadata': {},
|
||||
}
|
||||
)
|
||||
return
|
||||
|
||||
# Try to infer from content metadata
|
||||
if content.metadata:
|
||||
# Fallback: Try to infer from content metadata
|
||||
if hasattr(content, 'metadata') and content.metadata:
|
||||
cluster_id = content.metadata.get('cluster_id')
|
||||
if cluster_id:
|
||||
from igny8_core.business.planning.models import Clusters
|
||||
|
||||
@@ -37,20 +37,20 @@ class ContentValidationService:
|
||||
})
|
||||
|
||||
# Stage 3: Validate entity_type is set
|
||||
if not task.entity_type:
|
||||
if not task.content_type:
|
||||
errors.append({
|
||||
'field': 'entity_type',
|
||||
'code': 'missing_entity_type',
|
||||
'message': 'Task must have an entity type specified',
|
||||
'field': 'content_type',
|
||||
'code': 'missing_content_type',
|
||||
'message': 'Task must have a content type specified',
|
||||
})
|
||||
|
||||
# Stage 3: Validate taxonomy for product/service entities
|
||||
if task.entity_type in ['product', 'service']:
|
||||
if not task.taxonomy:
|
||||
if task.content_type in ['product', 'service']:
|
||||
if not task.taxonomy_term:
|
||||
errors.append({
|
||||
'field': 'taxonomy',
|
||||
'code': 'missing_taxonomy',
|
||||
'message': f'{task.entity_type.title()} tasks require a taxonomy association',
|
||||
'message': f'{task.content_type.title()} tasks require a taxonomy association',
|
||||
})
|
||||
|
||||
return errors
|
||||
@@ -67,12 +67,12 @@ class ContentValidationService:
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# Stage 3: Validate entity_type
|
||||
if not content.entity_type:
|
||||
# Stage 3: Validate content_type
|
||||
if not content.content_type:
|
||||
errors.append({
|
||||
'field': 'entity_type',
|
||||
'code': 'missing_entity_type',
|
||||
'message': 'Content must have an entity type specified',
|
||||
'field': 'content_type',
|
||||
'code': 'missing_content_type',
|
||||
'message': 'Content must have a content type specified',
|
||||
})
|
||||
|
||||
# Stage 3: Validate cluster mapping exists for IGNY8 content
|
||||
@@ -86,30 +86,20 @@ class ContentValidationService:
|
||||
})
|
||||
|
||||
# Stage 3: Validate taxonomy for product/service content
|
||||
if content.entity_type in ['product', 'service']:
|
||||
from igny8_core.business.content.models import ContentTaxonomyMap
|
||||
if not ContentTaxonomyMap.objects.filter(content=content).exists():
|
||||
if content.content_type in ['product', 'service']:
|
||||
# Check if content has taxonomy terms assigned
|
||||
if not content.taxonomy_terms.exists():
|
||||
errors.append({
|
||||
'field': 'taxonomy_mapping',
|
||||
'code': 'missing_taxonomy_mapping',
|
||||
'message': f'{content.entity_type.title()} content requires a taxonomy mapping',
|
||||
'message': f'{content.content_type.title()} content requires a taxonomy mapping',
|
||||
})
|
||||
|
||||
# Stage 3: Validate required attributes for products
|
||||
if content.entity_type == 'product':
|
||||
from igny8_core.business.content.models import ContentAttributeMap
|
||||
required_attrs = ['price', 'sku', 'category']
|
||||
existing_attrs = ContentAttributeMap.objects.filter(
|
||||
content=content,
|
||||
name__in=required_attrs
|
||||
).values_list('name', flat=True)
|
||||
missing_attrs = set(required_attrs) - set(existing_attrs)
|
||||
if missing_attrs:
|
||||
errors.append({
|
||||
'field': 'attributes',
|
||||
'code': 'missing_attributes',
|
||||
'message': f'Product content requires attributes: {", ".join(missing_attrs)}',
|
||||
})
|
||||
if content.content_type == 'product':
|
||||
# Product validation - currently simplified in Stage 1
|
||||
# TODO: Re-enable attribute validation when product attributes are implemented
|
||||
pass
|
||||
|
||||
return errors
|
||||
|
||||
@@ -136,9 +126,9 @@ class ContentValidationService:
|
||||
'message': 'Content must have a title before publishing',
|
||||
})
|
||||
|
||||
if not content.html_content or len(content.html_content.strip()) < 100:
|
||||
if not content.content_html or len(content.content_html.strip()) < 100:
|
||||
errors.append({
|
||||
'field': 'html_content',
|
||||
'field': 'content_html',
|
||||
'code': 'insufficient_content',
|
||||
'message': 'Content must have at least 100 characters before publishing',
|
||||
})
|
||||
@@ -157,9 +147,9 @@ class ContentValidationService:
|
||||
"""
|
||||
errors = []
|
||||
|
||||
if task.entity_type == 'product':
|
||||
if task.content_type == 'product':
|
||||
# Products should have taxonomy and cluster
|
||||
if not task.taxonomy:
|
||||
if not task.taxonomy_term:
|
||||
errors.append({
|
||||
'field': 'taxonomy',
|
||||
'code': 'missing_taxonomy',
|
||||
|
||||
@@ -348,19 +348,19 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
# Build response with synced counts
|
||||
post_types_data = {}
|
||||
for wp_type, type_config in content_types.get('post_types', {}).items():
|
||||
# Map WP type to entity_type
|
||||
entity_type_map = {
|
||||
# Map WP type to content_type
|
||||
content_type_map = {
|
||||
'post': 'post',
|
||||
'page': 'page',
|
||||
'product': 'product',
|
||||
'service': 'service',
|
||||
}
|
||||
entity_type = entity_type_map.get(wp_type, 'post')
|
||||
content_type = content_type_map.get(wp_type, 'post')
|
||||
|
||||
# Count synced content
|
||||
synced_count = Content.objects.filter(
|
||||
site=site,
|
||||
entity_type=entity_type,
|
||||
content_type=content_type,
|
||||
external_type=wp_type,
|
||||
sync_status__in=['imported', 'synced']
|
||||
).count()
|
||||
@@ -379,8 +379,7 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
# Count synced taxonomies
|
||||
synced_count = ContentTaxonomy.objects.filter(
|
||||
site=site,
|
||||
external_taxonomy=wp_tax,
|
||||
sync_status__in=['imported', 'synced']
|
||||
external_taxonomy=wp_tax
|
||||
).count()
|
||||
|
||||
taxonomies_data[wp_tax] = {
|
||||
|
||||
@@ -102,13 +102,14 @@ class ClusterSerializer(serializers.ModelSerializer):
|
||||
return ContentIdeas.objects.filter(keyword_cluster_id=obj.id).count()
|
||||
|
||||
def get_content_count(self, obj):
|
||||
"""Count generated content items linked to this cluster via tasks"""
|
||||
"""Count generated content items linked to this cluster"""
|
||||
if hasattr(obj, '_content_count'):
|
||||
return obj._content_count
|
||||
|
||||
from igny8_core.modules.writer.models import Content
|
||||
|
||||
return Content.objects.filter(task__cluster_id=obj.id).count()
|
||||
# Content links directly to clusters now (task field was removed in refactor)
|
||||
return Content.objects.filter(cluster_id=obj.id).count()
|
||||
|
||||
@classmethod
|
||||
def prefetch_keyword_stats(cls, clusters):
|
||||
@@ -166,16 +167,16 @@ class ClusterSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
idea_stats = {item['keyword_cluster_id']: item['count'] for item in idea_counts}
|
||||
|
||||
# Prefetch content counts (through writer.Tasks -> Content)
|
||||
# Prefetch content counts (Content links directly to Clusters now)
|
||||
from igny8_core.modules.writer.models import Content
|
||||
|
||||
content_counts = (
|
||||
Content.objects
|
||||
.filter(task__cluster_id__in=cluster_ids)
|
||||
.values('task__cluster_id')
|
||||
.filter(cluster_id__in=cluster_ids)
|
||||
.values('cluster_id')
|
||||
.annotate(count=Count('id'))
|
||||
)
|
||||
content_stats = {item['task__cluster_id']: item['count'] for item in content_counts}
|
||||
content_stats = {item['cluster_id']: item['count'] for item in content_counts}
|
||||
|
||||
# Attach stats to each cluster object
|
||||
for cluster in clusters:
|
||||
|
||||
@@ -102,10 +102,6 @@ class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
('Content', {
|
||||
'fields': ('content_html',)
|
||||
}),
|
||||
('Taxonomy', {
|
||||
'fields': ('taxonomy_terms',),
|
||||
'description': 'Categories, tags, and other taxonomy terms'
|
||||
}),
|
||||
('WordPress Sync', {
|
||||
'fields': ('external_id', 'external_url'),
|
||||
'classes': ('collapse',)
|
||||
|
||||
@@ -488,14 +488,12 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
# Update by content_id if provided, otherwise by image IDs
|
||||
if content_id:
|
||||
try:
|
||||
# Get the content object to also update images linked via task
|
||||
# Get the content object to also update images linked directly to content
|
||||
content = Content.objects.get(id=content_id)
|
||||
|
||||
# Update images linked directly to content OR via task (same logic as content_images endpoint)
|
||||
# This ensures we update all images: featured + 1-6 in-article images
|
||||
updated_count = queryset.filter(
|
||||
Q(content=content) | Q(task=content.task)
|
||||
).update(status=status_value)
|
||||
# Update images linked directly to content (all images: featured + in-article)
|
||||
# Note: task field was removed in refactor - images now link directly to content
|
||||
updated_count = queryset.filter(content=content).update(status=status_value)
|
||||
except Content.DoesNotExist:
|
||||
return error_response(
|
||||
error='Content not found',
|
||||
@@ -670,8 +668,8 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
'validation_errors': errors,
|
||||
'publish_errors': publish_errors,
|
||||
'metadata': {
|
||||
'has_entity_type': bool(content.entity_type),
|
||||
'entity_type': content.entity_type,
|
||||
'has_entity_type': bool(content.content_type),
|
||||
'entity_type': content.content_type,
|
||||
'has_cluster_mapping': self._has_cluster_mapping(content),
|
||||
'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
|
||||
}
|
||||
@@ -691,9 +689,9 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
validation_service = ContentValidationService()
|
||||
|
||||
# Persist metadata mappings if task exists
|
||||
if content.task:
|
||||
mapping_service = MetadataMappingService()
|
||||
mapping_service.persist_task_metadata_to_content(content)
|
||||
# Metadata is now persisted directly on content - no task linkage needed
|
||||
# mapping_service = MetadataMappingService() # DEPRECATED
|
||||
# mapping_service.persist_task_metadata_to_content(content) # DEPRECATED
|
||||
|
||||
errors = validation_service.validate_for_publish(content)
|
||||
|
||||
@@ -970,8 +968,8 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
'validation_errors': errors,
|
||||
'publish_errors': publish_errors,
|
||||
'metadata': {
|
||||
'has_entity_type': bool(content.entity_type),
|
||||
'entity_type': content.entity_type,
|
||||
'has_entity_type': bool(content.content_type),
|
||||
'entity_type': content.content_type,
|
||||
'has_cluster_mapping': self._has_cluster_mapping(content),
|
||||
'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
|
||||
}
|
||||
@@ -991,9 +989,9 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
validation_service = ContentValidationService()
|
||||
|
||||
# Persist metadata mappings if task exists
|
||||
if content.task:
|
||||
mapping_service = MetadataMappingService()
|
||||
mapping_service.persist_task_metadata_to_content(content)
|
||||
# Metadata is now persisted directly on content - no task linkage needed
|
||||
# mapping_service = MetadataMappingService() # DEPRECATED
|
||||
# mapping_service.persist_task_metadata_to_content(content) # DEPRECATED
|
||||
|
||||
errors = validation_service.validate_for_publish(content)
|
||||
|
||||
@@ -1121,8 +1119,8 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
'validation_errors': errors,
|
||||
'publish_errors': publish_errors,
|
||||
'metadata': {
|
||||
'has_entity_type': bool(content.entity_type),
|
||||
'entity_type': content.entity_type,
|
||||
'has_entity_type': bool(content.content_type),
|
||||
'entity_type': content.content_type,
|
||||
'has_cluster_mapping': self._has_cluster_mapping(content),
|
||||
'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
|
||||
}
|
||||
@@ -1142,9 +1140,9 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
validation_service = ContentValidationService()
|
||||
|
||||
# Persist metadata mappings if task exists
|
||||
if content.task:
|
||||
mapping_service = MetadataMappingService()
|
||||
mapping_service.persist_task_metadata_to_content(content)
|
||||
# Metadata is now persisted directly on content - no task linkage needed
|
||||
# mapping_service = MetadataMappingService() # DEPRECATED
|
||||
# mapping_service.persist_task_metadata_to_content(content) # DEPRECATED
|
||||
|
||||
errors = validation_service.validate_for_publish(content)
|
||||
|
||||
@@ -1272,8 +1270,8 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
'validation_errors': errors,
|
||||
'publish_errors': publish_errors,
|
||||
'metadata': {
|
||||
'has_entity_type': bool(content.entity_type),
|
||||
'entity_type': content.entity_type,
|
||||
'has_entity_type': bool(content.content_type),
|
||||
'entity_type': content.content_type,
|
||||
'has_cluster_mapping': self._has_cluster_mapping(content),
|
||||
'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
|
||||
}
|
||||
@@ -1293,9 +1291,9 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
validation_service = ContentValidationService()
|
||||
|
||||
# Persist metadata mappings if task exists
|
||||
if content.task:
|
||||
mapping_service = MetadataMappingService()
|
||||
mapping_service.persist_task_metadata_to_content(content)
|
||||
# Metadata is now persisted directly on content - no task linkage needed
|
||||
# mapping_service = MetadataMappingService() # DEPRECATED
|
||||
# mapping_service.persist_task_metadata_to_content(content) # DEPRECATED
|
||||
|
||||
errors = validation_service.validate_for_publish(content)
|
||||
|
||||
@@ -1422,8 +1420,8 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
'validation_errors': errors,
|
||||
'publish_errors': publish_errors,
|
||||
'metadata': {
|
||||
'has_entity_type': bool(content.entity_type),
|
||||
'entity_type': content.entity_type,
|
||||
'has_entity_type': bool(content.content_type),
|
||||
'entity_type': content.content_type,
|
||||
'has_cluster_mapping': self._has_cluster_mapping(content),
|
||||
'has_taxonomy_mapping': self._has_taxonomy_mapping(content),
|
||||
}
|
||||
@@ -1443,9 +1441,9 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
validation_service = ContentValidationService()
|
||||
|
||||
# Persist metadata mappings if task exists
|
||||
if content.task:
|
||||
mapping_service = MetadataMappingService()
|
||||
mapping_service.persist_task_metadata_to_content(content)
|
||||
# Metadata is now persisted directly on content - no task linkage needed
|
||||
# mapping_service = MetadataMappingService() # DEPRECATED
|
||||
# mapping_service.persist_task_metadata_to_content(content) # DEPRECATED
|
||||
|
||||
errors = validation_service.validate_for_publish(content)
|
||||
|
||||
@@ -1466,7 +1464,7 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
def _has_taxonomy_mapping(self, content):
|
||||
"""Helper to check if content has taxonomy mapping"""
|
||||
# Check new M2M relationship
|
||||
return content.taxonomies.exists()
|
||||
return content.taxonomy_terms.exists()
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
|
||||
1597
backend/igny8_core/modules/writer/views.py.bak
Normal file
1597
backend/igny8_core/modules/writer/views.py.bak
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user