Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts

This commit is contained in:
ajaysi
2026-02-08 13:56:57 +05:30
parent 1db10ccd0f
commit e404a86502
333 changed files with 42223 additions and 10875 deletions

View File

@@ -20,7 +20,7 @@ class WebsiteAnalysisService:
"""Initialize the service with database session."""
self.db = db_session
def save_analysis(self, session_id: int, website_url: str, analysis_data: Dict[str, Any]) -> Optional[int]:
def save_analysis(self, session_id: int, website_url: str, analysis_data: Dict[str, Any], preserve_persona: bool = False) -> Optional[int]:
"""
Save website analysis results to database.
@@ -28,6 +28,8 @@ class WebsiteAnalysisService:
session_id: Onboarding session ID
website_url: The analyzed website URL
analysis_data: Complete analysis results from style detection
preserve_persona: If True, existing brand persona fields (writing_style, target_audience, etc.)
will NOT be overwritten if they already contain data.
Returns:
Analysis ID if successful, None otherwise
@@ -42,30 +44,77 @@ class WebsiteAnalysisService:
if existing_analysis:
# Update existing analysis
style_analysis = analysis_data.get('style_analysis', {})
existing_analysis.writing_style = style_analysis.get('writing_style')
existing_analysis.content_characteristics = style_analysis.get('content_characteristics')
existing_analysis.target_audience = style_analysis.get('target_audience')
existing_analysis.content_type = style_analysis.get('content_type')
existing_analysis.recommended_settings = style_analysis.get('recommended_settings')
# Prepare crawl_result with extra data to ensure no data loss
crawl_result = analysis_data.get('crawl_result') or {}
if not isinstance(crawl_result, dict):
crawl_result = {"raw": crawl_result}
# Store extra fields in crawl_result if columns don't exist
if style_analysis.get('meta_info'):
crawl_result['meta_info'] = style_analysis.get('meta_info')
# Store sitemap_analysis in crawl_result since it doesn't have its own column
if analysis_data.get('sitemap_analysis'):
crawl_result['sitemap_analysis'] = analysis_data.get('sitemap_analysis')
# Update persona fields only if not preserving or if they are empty
if not preserve_persona or not existing_analysis.writing_style:
existing_analysis.writing_style = style_analysis.get('writing_style')
if not preserve_persona or not existing_analysis.content_characteristics:
existing_analysis.content_characteristics = style_analysis.get('content_characteristics')
if not preserve_persona or not existing_analysis.target_audience:
existing_analysis.target_audience = style_analysis.get('target_audience')
if not preserve_persona or not existing_analysis.content_type:
existing_analysis.content_type = style_analysis.get('content_type')
if not preserve_persona or not existing_analysis.recommended_settings:
existing_analysis.recommended_settings = style_analysis.get('recommended_settings')
# Store brand_analysis and content_strategy_insights if model supports it
if hasattr(existing_analysis, 'brand_analysis'):
existing_analysis.brand_analysis = style_analysis.get('brand_analysis')
if not preserve_persona or not existing_analysis.brand_analysis:
existing_analysis.brand_analysis = style_analysis.get('brand_analysis')
if hasattr(existing_analysis, 'content_strategy_insights'):
existing_analysis.content_strategy_insights = style_analysis.get('content_strategy_insights')
existing_analysis.crawl_result = analysis_data.get('crawl_result')
# Strategy insights are more dynamic, but arguably part of persona.
# Let's preserve them too if requested, as user might have edited them.
if not preserve_persona or not existing_analysis.content_strategy_insights:
existing_analysis.content_strategy_insights = style_analysis.get('content_strategy_insights')
# Always update technical/factual fields
existing_analysis.crawl_result = crawl_result
existing_analysis.style_patterns = analysis_data.get('style_patterns')
existing_analysis.style_guidelines = analysis_data.get('style_guidelines')
existing_analysis.seo_audit = analysis_data.get('seo_audit')
existing_analysis.status = 'completed'
existing_analysis.error_message = None
existing_analysis.warning_message = analysis_data.get('warning')
existing_analysis.updated_at = datetime.utcnow()
self.db.commit()
logger.info(f"Updated existing analysis for URL: {website_url}")
logger.info(f"Updated existing analysis for URL: {website_url} (preserve_persona={preserve_persona})")
return existing_analysis.id
else:
# Create new analysis
style_analysis = analysis_data.get('style_analysis', {})
# Prepare crawl_result with extra data
crawl_result = analysis_data.get('crawl_result') or {}
if not isinstance(crawl_result, dict):
crawl_result = {"raw": crawl_result}
# Store extra fields in crawl_result
if style_analysis.get('meta_info'):
crawl_result['meta_info'] = style_analysis.get('meta_info')
# Store sitemap_analysis in crawl_result since it doesn't have its own column
if analysis_data.get('sitemap_analysis'):
crawl_result['sitemap_analysis'] = analysis_data.get('sitemap_analysis')
analysis_args = {
'session_id': session_id,
'website_url': website_url,
@@ -74,9 +123,10 @@ class WebsiteAnalysisService:
'target_audience': style_analysis.get('target_audience'),
'content_type': style_analysis.get('content_type'),
'recommended_settings': style_analysis.get('recommended_settings'),
'crawl_result': analysis_data.get('crawl_result'),
'crawl_result': crawl_result,
'style_patterns': analysis_data.get('style_patterns'),
'style_guidelines': analysis_data.get('style_guidelines'),
'seo_audit': analysis_data.get('seo_audit'),
'status': 'completed',
'warning_message': analysis_data.get('warning')
}
@@ -246,6 +296,68 @@ class WebsiteAnalysisService:
logger.error(f"Error deleting analysis {analysis_id}: {str(e)}")
return False
def update_analysis_content(self, analysis_id: int, analysis_data: Dict[str, Any]) -> bool:
"""
Update specific content fields of an existing analysis.
Args:
analysis_id: Analysis ID to update
analysis_data: Dictionary containing fields to update (writing_style, etc.)
Returns:
True if successful, False otherwise
"""
try:
analysis = self.db.query(WebsiteAnalysis).get(analysis_id)
if not analysis:
logger.warning(f"Analysis {analysis_id} not found for update")
return False
# Update fields if present in data
if 'writing_style' in analysis_data:
analysis.writing_style = analysis_data['writing_style']
if 'content_characteristics' in analysis_data:
analysis.content_characteristics = analysis_data['content_characteristics']
if 'target_audience' in analysis_data:
analysis.target_audience = analysis_data['target_audience']
if 'content_type' in analysis_data:
analysis.content_type = analysis_data['content_type']
if 'recommended_settings' in analysis_data:
analysis.recommended_settings = analysis_data['recommended_settings']
# Optional fields
if 'brand_analysis' in analysis_data and hasattr(analysis, 'brand_analysis'):
analysis.brand_analysis = analysis_data['brand_analysis']
if 'content_strategy_insights' in analysis_data and hasattr(analysis, 'content_strategy_insights'):
analysis.content_strategy_insights = analysis_data['content_strategy_insights']
# Update guidelines if present (nested in style_guidelines usually)
# But the frontend might send them separately or as part of a guidelines object
# If the frontend sends the whole 'analysis' object structure, we might need to map it back
# to style_guidelines structure if that's how it's stored.
# Based on save_analysis, style_guidelines is a JSON field.
# If the frontend sends 'guidelines', 'best_practices' etc. separately (flattened),
# we need to reconstruct style_guidelines or update the existing one.
# Let's assume the frontend sends the same structure as it received or we handle the mapping in the API layer.
# For now, let's support direct update of style_guidelines if provided
if 'style_guidelines' in analysis_data:
analysis.style_guidelines = analysis_data['style_guidelines']
# Update SEO audit if present
if 'seo_audit' in analysis_data:
analysis.seo_audit = analysis_data['seo_audit']
analysis.updated_at = datetime.utcnow()
self.db.commit()
logger.info(f"Updated content for analysis {analysis_id}")
return True
except SQLAlchemyError as e:
self.db.rollback()
logger.error(f"Error updating analysis {analysis_id}: {str(e)}")
return False
def save_error_analysis(self, session_id: int, website_url: str, error_message: str) -> Optional[int]:
"""
Save analysis record with error status.
@@ -274,4 +386,58 @@ class WebsiteAnalysisService:
except SQLAlchemyError as e:
self.db.rollback()
logger.error(f"Error saving error analysis: {str(e)}")
return None
return None
def update_analysis_content(self, analysis_id: int, analysis_data: Dict[str, Any]) -> bool:
"""
Update specific content fields of an existing analysis.
Args:
analysis_id: Analysis ID to update
analysis_data: Dictionary containing fields to update (writing_style, etc.)
Returns:
True if successful, False otherwise
"""
try:
analysis = self.db.query(WebsiteAnalysis).get(analysis_id)
if not analysis:
logger.warning(f"Analysis {analysis_id} not found for update")
return False
# Update fields if present in data
if 'writing_style' in analysis_data:
analysis.writing_style = analysis_data['writing_style']
if 'content_characteristics' in analysis_data:
analysis.content_characteristics = analysis_data['content_characteristics']
if 'target_audience' in analysis_data:
analysis.target_audience = analysis_data['target_audience']
if 'content_type' in analysis_data:
analysis.content_type = analysis_data['content_type']
if 'recommended_settings' in analysis_data:
analysis.recommended_settings = analysis_data['recommended_settings']
# Optional fields
if 'brand_analysis' in analysis_data and hasattr(analysis, 'brand_analysis'):
analysis.brand_analysis = analysis_data['brand_analysis']
if 'content_strategy_insights' in analysis_data and hasattr(analysis, 'content_strategy_insights'):
analysis.content_strategy_insights = analysis_data['content_strategy_insights']
# Update style_guidelines if provided
if 'style_guidelines' in analysis_data:
analysis.style_guidelines = analysis_data['style_guidelines']
# Update SEO audit if provided
if 'seo_audit' in analysis_data:
analysis.seo_audit = analysis_data['seo_audit']
analysis.updated_at = datetime.utcnow()
self.db.commit()
logger.info(f"Updated content for analysis {analysis_id}")
return True
except SQLAlchemyError as e:
self.db.rollback()
logger.error(f"Error updating analysis {analysis_id}: {str(e)}")
return False