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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user