AI functins complete

This commit is contained in:
IGNY8 VPS (Salman)
2025-11-26 20:55:03 +00:00
parent 94a8aee0e2
commit 9b9352b9d2
4 changed files with 68 additions and 23 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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',

View File

@@ -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
</h3>
<div className="space-y-3">
{content.meta_title && (
<div>
<p className="text-xs text-gray-500 dark:text-gray-400">Meta Title</p>
<p className="text-sm font-medium text-gray-900 dark:text-white">
{content.meta_title}
</p>
</div>
)}
{content.meta_description && (
<div>
<p className="text-xs text-gray-500 dark:text-gray-400">Meta Description</p>
<p className="text-sm text-gray-700 dark:text-gray-300">
{content.meta_description}
</p>
</div>
)}
{content.primary_keyword && (
<div>
<p className="text-xs text-gray-500 dark:text-gray-400">Primary Keyword</p>
@@ -797,35 +813,39 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
</div>
</div>
{/* 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 ? (
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
<div className="flex flex-wrap gap-4">
{content.tags && content.tags.length > 0 && (
{content.taxonomy_terms_data.filter(term => term.taxonomy_type === 'tag').length > 0 && (
<div className="flex items-center gap-2">
<TagIcon className="w-4 h-4 text-gray-400" />
<div className="flex flex-wrap gap-2">
{content.tags.map((tag, idx) => (
{content.taxonomy_terms_data
.filter(term => term.taxonomy_type === 'tag')
.map((tag) => (
<span
key={idx}
key={tag.id}
className="px-3 py-1 bg-brand-50 dark:bg-brand-900/20 text-brand-700 dark:text-brand-300 rounded-full text-xs font-medium"
>
{tag}
{tag.name}
</span>
))}
</div>
</div>
)}
{content.categories && content.categories.length > 0 && (
{content.taxonomy_terms_data.filter(term => term.taxonomy_type === 'category').length > 0 && (
<div className="flex items-center gap-2">
<span className="text-xs text-gray-500 dark:text-gray-400">Categories:</span>
<div className="flex flex-wrap gap-2">
{content.categories.map((category, idx) => (
{content.taxonomy_terms_data
.filter(term => term.taxonomy_type === 'category')
.map((category) => (
<span
key={idx}
key={category.id}
className="px-3 py-1 bg-purple-50 dark:bg-purple-900/20 text-purple-700 dark:text-purple-300 rounded-full text-xs font-medium"
>
{category}
{category.name}
</span>
))}
</div>
@@ -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}
/>
</div>