Refactor content handling in GenerateContentFunction and update related models and serializers

- Enhanced GenerateContentFunction to save content in a dedicated Content model, separating it from the Tasks model.
- Updated Tasks model to remove SEO-related fields, now managed in the Content model.
- Modified TasksSerializer to include new content fields and adjusted the API to reflect these changes.
- Improved the auto_generate_content_task method to utilize the new save_output method for better content management.
- Updated frontend components to display new content structure and metadata effectively.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-10 14:06:15 +00:00
parent 8bb4c5d016
commit 8b6e18649c
11 changed files with 596 additions and 356 deletions

View File

@@ -632,103 +632,122 @@ def auto_generate_content_task(self, task_ids: List[int], account_id: int = None
}
)
# Normalize content from different AI response formats
logger.info(f" * Normalizing content (length: {len(content)} chars)...")
# Parse JSON response using GenerateContentFunction's parse_response method
logger.info(f" * Parsing AI response (length: {len(content)} chars)...")
try:
from igny8_core.utils.content_normalizer import normalize_content
normalized = normalize_content(content)
normalized_content = normalized['normalized_content']
content_type = normalized['content_type']
has_structure = normalized['has_structure']
original_format = normalized['original_format']
from igny8_core.ai.functions.generate_content import GenerateContentFunction
fn = GenerateContentFunction()
parsed_response = fn.parse_response(content)
logger.info(f" * ✓ Content normalized:")
logger.info(f" - Original format: {original_format}")
logger.info(f" - Content type: {content_type}")
logger.info(f" - Has structure: {has_structure}")
logger.info(f" - Normalized length: {len(normalized_content)} chars")
logger.info(f" - Normalized preview (first 200 chars): {normalized_content[:200]}...")
logger.info(f" * ✓ Response parsed:")
logger.info(f" - Type: {type(parsed_response).__name__}")
if isinstance(parsed_response, dict):
logger.info(f" - Keys: {list(parsed_response.keys())}")
logger.info(f" - Has title: {bool(parsed_response.get('title'))}")
logger.info(f" - Has meta_title: {bool(parsed_response.get('meta_title'))}")
logger.info(f" - Has primary_keyword: {bool(parsed_response.get('primary_keyword'))}")
logger.info(f" - Has secondary_keywords: {bool(parsed_response.get('secondary_keywords'))}")
logger.info(f" - Has tags: {bool(parsed_response.get('tags'))}")
logger.info(f" - Has categories: {bool(parsed_response.get('categories'))}")
logger.info(f" - Content length: {len(parsed_response.get('content', ''))} chars")
else:
logger.info(f" - Content length: {len(str(parsed_response))} chars")
# Use normalized content
content = normalized_content
# Use parsed response for saving
parsed_data = parsed_response
except Exception as norm_error:
logger.warning(f" * ⚠️ Content normalization failed: {type(norm_error).__name__}: {str(norm_error)}")
logger.warning(f" * Using original content as-is")
# Continue with original content
except Exception as parse_error:
logger.warning(f" * ⚠️ JSON parsing failed: {type(parse_error).__name__}: {str(parse_error)}")
logger.warning(f" * Treating as plain text content")
# Fallback to plain text
parsed_data = {'content': content}
# Normalize content from parsed response
content_to_normalize = parsed_data.get('content', '') if isinstance(parsed_data, dict) else str(parsed_data)
if content_to_normalize:
logger.info(f" * Normalizing content (length: {len(content_to_normalize)} chars)...")
try:
from igny8_core.utils.content_normalizer import normalize_content
normalized = normalize_content(content_to_normalize)
normalized_content = normalized['normalized_content']
content_type = normalized['content_type']
has_structure = normalized['has_structure']
original_format = normalized['original_format']
logger.info(f" * ✓ Content normalized:")
logger.info(f" - Original format: {original_format}")
logger.info(f" - Content type: {content_type}")
logger.info(f" - Has structure: {has_structure}")
logger.info(f" - Normalized length: {len(normalized_content)} chars")
logger.info(f" - Normalized preview (first 200 chars): {normalized_content[:200]}...")
# Update parsed_data with normalized content
if isinstance(parsed_data, dict):
parsed_data['content'] = normalized_content
else:
parsed_data = {'content': normalized_content}
except Exception as norm_error:
logger.warning(f" * ⚠️ Content normalization failed: {type(norm_error).__name__}: {str(norm_error)}")
logger.warning(f" * Using original content as-is")
# Continue with original content
except Exception as ai_error:
logger.error(f" * ✗ EXCEPTION during AI API call: {type(ai_error).__name__}: {str(ai_error)}")
logger.error(f" * Task ID: {task.id}", exc_info=True)
continue
# Calculate word count from normalized content
# Remove HTML tags for word count
text_for_counting = re.sub(r'<[^>]+>', '', content)
word_count = len(text_for_counting.split())
logger.info(f" * ✓ Word count calculated: {word_count} words (from normalized HTML)")
# Update progress: Saving content
add_step('SAVE', 'success', f"Saving content for '{task.title}' ({word_count} words)...", 'request')
save_pct = 85 + int((idx / total_tasks) * 10) # 85-95% for saving
self.update_state(
state='PROGRESS',
meta={
'current': idx + 1,
'total': total_tasks,
'percentage': save_pct,
'message': f"Saving content for '{task.title}' ({word_count} words)...",
'phase': 'SAVE',
'current_item': task.title,
'request_steps': request_steps,
'response_steps': response_steps
}
)
# ========================================================================
# DATABASE SAVE PHASE - Detailed logging
# ========================================================================
logger.info(" - Saving content to database...")
# Use GenerateContentFunction's save_output method to properly save all fields
logger.info(" - Saving content to database using GenerateContentFunction.save_output()...")
try:
# Update task
logger.info(f" * Updating task {task.id} fields...")
task.content = content
logger.info(f" - content: {len(content)} chars")
from igny8_core.ai.functions.generate_content import GenerateContentFunction
fn = GenerateContentFunction()
task.word_count = word_count
# Save using the proper save_output method which handles all fields
save_result = fn.save_output(parsed_data, [task], account)
# Get word count from save result or calculate
word_count = save_result.get('word_count', 0)
if not word_count and isinstance(parsed_data, dict):
content_for_count = parsed_data.get('content', '')
if content_for_count:
text_for_counting = re.sub(r'<[^>]+>', '', content_for_count)
word_count = len(text_for_counting.split())
logger.info(f" * ✓ Task saved successfully using save_output()")
logger.info(f" - tasks_updated: {save_result.get('tasks_updated', 0)}")
logger.info(f" - word_count: {word_count}")
task.meta_title = task.title # Use title as meta title for now
logger.info(f" - meta_title: {task.title}")
# Log all fields that were saved
logger.info(f" * Saved fields:")
logger.info(f" - task_id: {task.id}")
logger.info(f" - task_status: {task.status}")
if isinstance(parsed_data, dict):
logger.info(f" - content_title: {parsed_data.get('title') or task.title}")
logger.info(f" - content_primary_keyword: {parsed_data.get('primary_keyword') or 'N/A'}")
logger.info(f" - content_secondary_keywords: {len(parsed_data.get('secondary_keywords') or [])} items")
logger.info(f" - content_tags: {len(parsed_data.get('tags') or [])} items")
logger.info(f" - content_categories: {len(parsed_data.get('categories') or [])} items")
logger.info(f" - content_word_count: {word_count}")
task.meta_description = (task.description or '')[:160] # Truncate to 160 chars
logger.info(f" - meta_description: {len(task.meta_description)} chars")
# Update progress: Saving content
add_step('SAVE', 'success', f"Content saved for '{task.title}' ({word_count} words)...", 'response')
save_pct = 85 + int((idx / total_tasks) * 10) # 85-95% for saving
self.update_state(
state='PROGRESS',
meta={
'current': idx + 1,
'total': total_tasks,
'percentage': save_pct,
'message': f"Content saved for '{task.title}' ({word_count} words)...",
'phase': 'SAVE',
'current_item': task.title,
'request_steps': request_steps,
'response_steps': response_steps
}
)
old_status = task.status
task.status = 'draft' # Update status from queued to draft
logger.info(f" - status: {old_status}{task.status}")
# Log all fields being saved
logger.info(f" * Task fields to save:")
logger.info(f" - id: {task.id}")
logger.info(f" - title: {task.title}")
logger.info(f" - account_id: {task.account_id}")
logger.info(f" - site_id: {task.site_id}")
logger.info(f" - sector_id: {task.sector_id}")
logger.info(f" - cluster_id: {task.cluster_id}")
logger.info(f" - idea_id: {task.idea_id}")
logger.info(f" - content length: {len(task.content)}")
logger.info(f" - word_count: {task.word_count}")
# Save to database
logger.info(f" * Executing task.save()...")
task.save()
logger.info(f" * ✓ Task saved successfully to database")
# Mark save step as complete
add_step('SAVE', 'success', f"Content saved for '{task.title}'", 'response')
tasks_updated += 1
tasks_updated += save_result.get('tasks_updated', 0)
logger.info(f" * ✓ Task {task.id} content generation completed successfully")
except Exception as save_error: