From 9b9352b9d26a702082b4f28b679b90cc981504c7 Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Wed, 26 Nov 2025 20:55:03 +0000 Subject: [PATCH] AI functins complete --- .../ai/functions/generate_content.py | 14 ++++ backend/igny8_core/business/content/models.py | 7 ++ .../igny8_core/modules/writer/serializers.py | 4 ++ .../src/templates/ContentViewTemplate.tsx | 66 ++++++++++++------- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/backend/igny8_core/ai/functions/generate_content.py b/backend/igny8_core/ai/functions/generate_content.py index 9eddd26b..6afc57d2 100644 --- a/backend/igny8_core/ai/functions/generate_content.py +++ b/backend/igny8_core/ai/functions/generate_content.py @@ -175,10 +175,18 @@ class GenerateContentFunction(BaseAIFunction): # JSON response with structured fields content_html = parsed.get('content', '') title = parsed.get('title') or task.title + meta_title = parsed.get('meta_title') or parsed.get('seo_title') or title + meta_description = parsed.get('meta_description') or parsed.get('seo_description') + primary_keyword = parsed.get('primary_keyword') or parsed.get('focus_keyword') + secondary_keywords = parsed.get('secondary_keywords') or parsed.get('keywords', []) else: # Plain text response content_html = str(parsed) title = task.title + meta_title = title + meta_description = None + primary_keyword = None + secondary_keywords = [] # Calculate word count word_count = 0 @@ -192,6 +200,12 @@ class GenerateContentFunction(BaseAIFunction): title=title, content_html=content_html or '', word_count=word_count, + # SEO fields + meta_title=meta_title, + meta_description=meta_description, + primary_keyword=primary_keyword, + secondary_keywords=secondary_keywords if isinstance(secondary_keywords, list) else [], + # Structure cluster=task.cluster, content_type=task.content_type, content_structure=task.content_structure, diff --git a/backend/igny8_core/business/content/models.py b/backend/igny8_core/business/content/models.py index 65674a50..d6fddf8e 100644 --- a/backend/igny8_core/business/content/models.py +++ b/backend/igny8_core/business/content/models.py @@ -174,6 +174,13 @@ class Content(SiteSectorBaseModel): default=0, help_text="Actual word count of content (calculated from HTML)" ) + + # SEO fields + meta_title = models.CharField(max_length=255, blank=True, null=True, help_text="SEO meta title") + meta_description = models.TextField(blank=True, null=True, help_text="SEO meta description") + primary_keyword = models.CharField(max_length=255, blank=True, null=True, help_text="Primary SEO keyword") + secondary_keywords = models.JSONField(default=list, blank=True, help_text="Secondary SEO keywords") + cluster = models.ForeignKey( 'planner.Clusters', on_delete=models.SET_NULL, diff --git a/backend/igny8_core/modules/writer/serializers.py b/backend/igny8_core/modules/writer/serializers.py index 2c735486..37e033e7 100644 --- a/backend/igny8_core/modules/writer/serializers.py +++ b/backend/igny8_core/modules/writer/serializers.py @@ -163,6 +163,10 @@ class ContentSerializer(serializers.ModelSerializer): 'id', 'title', 'content_html', + 'meta_title', + 'meta_description', + 'primary_keyword', + 'secondary_keywords', 'cluster_id', 'cluster_name', 'content_type', diff --git a/frontend/src/templates/ContentViewTemplate.tsx b/frontend/src/templates/ContentViewTemplate.tsx index 7b9969d0..5838f87f 100644 --- a/frontend/src/templates/ContentViewTemplate.tsx +++ b/frontend/src/templates/ContentViewTemplate.tsx @@ -585,8 +585,8 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten ]); const parsedArticle = useMemo( - () => parseContentHtml(content?.html_content ?? ''), - [content?.html_content] + () => parseContentHtml(content?.content_html ?? ''), + [content?.content_html] ); const shouldShowFeaturedBlock = imagesLoading || Boolean(resolvedFeaturedImage); @@ -770,6 +770,22 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten SEO & Tags
+ {content.meta_title && ( +
+

Meta Title

+

+ {content.meta_title} +

+
+ )} + {content.meta_description && ( +
+

Meta Description

+

+ {content.meta_description} +

+
+ )} {content.primary_keyword && (

Primary Keyword

@@ -797,37 +813,41 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
- {/* Tags and Categories */} - {(content.tags && content.tags.length > 0) || (content.categories && content.categories.length > 0) ? ( + {/* Tags and Categories from taxonomy_terms_data */} + {content.taxonomy_terms_data && content.taxonomy_terms_data.length > 0 ? (
- {content.tags && content.tags.length > 0 && ( + {content.taxonomy_terms_data.filter(term => term.taxonomy_type === 'tag').length > 0 && (
- {content.tags.map((tag, idx) => ( - - {tag} - - ))} + {content.taxonomy_terms_data + .filter(term => term.taxonomy_type === 'tag') + .map((tag) => ( + + {tag.name} + + ))}
)} - {content.categories && content.categories.length > 0 && ( + {content.taxonomy_terms_data.filter(term => term.taxonomy_type === 'category').length > 0 && (
Categories:
- {content.categories.map((category, idx) => ( - - {category} - - ))} + {content.taxonomy_terms_data + .filter(term => term.taxonomy_type === 'category') + .map((category) => ( + + {category.name} + + ))}
)} @@ -878,7 +898,7 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten sections={parsedArticle.sections} sectionImages={resolvedInArticleImages} imagesLoading={imagesLoading} - rawHtml={content.html_content} + rawHtml={content.content_html} />