Base code
This commit is contained in:
277
backend/services/website_analysis_service.py
Normal file
277
backend/services/website_analysis_service.py
Normal file
@@ -0,0 +1,277 @@
|
||||
"""
|
||||
Website Analysis Service for Onboarding Step 2
|
||||
Handles storage and retrieval of website analysis results.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional, List
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from datetime import datetime
|
||||
import json
|
||||
from loguru import logger
|
||||
|
||||
from models.onboarding import WebsiteAnalysis, OnboardingSession
|
||||
|
||||
|
||||
class WebsiteAnalysisService:
|
||||
"""Service for managing website analysis data during onboarding."""
|
||||
|
||||
def __init__(self, db_session: Session):
|
||||
"""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]:
|
||||
"""
|
||||
Save website analysis results to database.
|
||||
|
||||
Args:
|
||||
session_id: Onboarding session ID
|
||||
website_url: The analyzed website URL
|
||||
analysis_data: Complete analysis results from style detection
|
||||
|
||||
Returns:
|
||||
Analysis ID if successful, None otherwise
|
||||
"""
|
||||
try:
|
||||
# Check if analysis already exists for this URL and session
|
||||
existing_analysis = self.db.query(WebsiteAnalysis).filter_by(
|
||||
session_id=session_id,
|
||||
website_url=website_url
|
||||
).first()
|
||||
|
||||
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')
|
||||
# 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 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')
|
||||
existing_analysis.style_patterns = analysis_data.get('style_patterns')
|
||||
existing_analysis.style_guidelines = analysis_data.get('style_guidelines')
|
||||
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}")
|
||||
return existing_analysis.id
|
||||
else:
|
||||
# Create new analysis
|
||||
style_analysis = analysis_data.get('style_analysis', {})
|
||||
analysis_args = {
|
||||
'session_id': session_id,
|
||||
'website_url': website_url,
|
||||
'writing_style': style_analysis.get('writing_style'),
|
||||
'content_characteristics': style_analysis.get('content_characteristics'),
|
||||
'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'),
|
||||
'style_patterns': analysis_data.get('style_patterns'),
|
||||
'style_guidelines': analysis_data.get('style_guidelines'),
|
||||
'status': 'completed',
|
||||
'warning_message': analysis_data.get('warning')
|
||||
}
|
||||
# Add brand_analysis and content_strategy_insights if model supports it
|
||||
if hasattr(WebsiteAnalysis, 'brand_analysis'):
|
||||
analysis_args['brand_analysis'] = style_analysis.get('brand_analysis')
|
||||
if hasattr(WebsiteAnalysis, 'content_strategy_insights'):
|
||||
analysis_args['content_strategy_insights'] = style_analysis.get('content_strategy_insights')
|
||||
|
||||
analysis = WebsiteAnalysis(**analysis_args)
|
||||
|
||||
self.db.add(analysis)
|
||||
self.db.commit()
|
||||
logger.info(f"Saved new analysis for URL: {website_url}")
|
||||
return analysis.id
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"Error saving website analysis: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_analysis(self, analysis_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Retrieve website analysis by ID.
|
||||
|
||||
Args:
|
||||
analysis_id: Analysis ID
|
||||
|
||||
Returns:
|
||||
Analysis data dictionary or None if not found
|
||||
"""
|
||||
try:
|
||||
analysis = self.db.query(WebsiteAnalysis).get(analysis_id)
|
||||
if analysis:
|
||||
return analysis.to_dict()
|
||||
return None
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error retrieving analysis {analysis_id}: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_analysis_by_url(self, session_id: int, website_url: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get analysis for a specific URL in a session.
|
||||
|
||||
Args:
|
||||
session_id: Onboarding session ID
|
||||
website_url: Website URL
|
||||
|
||||
Returns:
|
||||
Analysis data dictionary or None if not found
|
||||
"""
|
||||
try:
|
||||
analysis = self.db.query(WebsiteAnalysis).filter_by(
|
||||
session_id=session_id,
|
||||
website_url=website_url
|
||||
).first()
|
||||
|
||||
if analysis:
|
||||
return analysis.to_dict()
|
||||
return None
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error retrieving analysis for URL {website_url}: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_session_analyses(self, session_id: int) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get all analyses for a session.
|
||||
|
||||
Args:
|
||||
session_id: Onboarding session ID
|
||||
|
||||
Returns:
|
||||
List of analysis summaries
|
||||
"""
|
||||
try:
|
||||
analyses = self.db.query(WebsiteAnalysis).filter_by(
|
||||
session_id=session_id
|
||||
).order_by(WebsiteAnalysis.created_at.desc()).all()
|
||||
|
||||
return [analysis.to_dict() for analysis in analyses]
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error retrieving analyses for session {session_id}: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_analysis_by_session(self, session_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get the latest analysis for a session.
|
||||
|
||||
Args:
|
||||
session_id: Onboarding session ID
|
||||
|
||||
Returns:
|
||||
Latest analysis data or None if not found
|
||||
"""
|
||||
try:
|
||||
analysis = self.db.query(WebsiteAnalysis).filter_by(
|
||||
session_id=session_id
|
||||
).order_by(WebsiteAnalysis.created_at.desc()).first()
|
||||
|
||||
if analysis:
|
||||
return analysis.to_dict()
|
||||
return None
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error retrieving latest analysis for session {session_id}: {str(e)}")
|
||||
return None
|
||||
|
||||
def check_existing_analysis(self, session_id: int, website_url: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Check if analysis exists for a URL and return it if found.
|
||||
Used for confirmation dialog in frontend.
|
||||
|
||||
Args:
|
||||
session_id: Onboarding session ID
|
||||
website_url: Website URL
|
||||
|
||||
Returns:
|
||||
Analysis data if found, None otherwise
|
||||
"""
|
||||
try:
|
||||
analysis = self.db.query(WebsiteAnalysis).filter_by(
|
||||
session_id=session_id,
|
||||
website_url=website_url
|
||||
).first()
|
||||
|
||||
if analysis and analysis.status == 'completed':
|
||||
return {
|
||||
'exists': True,
|
||||
'analysis_date': analysis.analysis_date.isoformat() if analysis.analysis_date else None,
|
||||
'analysis_id': analysis.id,
|
||||
'summary': {
|
||||
'writing_style': analysis.writing_style,
|
||||
'target_audience': analysis.target_audience,
|
||||
'content_type': analysis.content_type
|
||||
}
|
||||
}
|
||||
return {'exists': False}
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
logger.error(f"Error checking existing analysis for URL {website_url}: {str(e)}")
|
||||
return {'exists': False, 'error': str(e)}
|
||||
|
||||
def delete_analysis(self, analysis_id: int) -> bool:
|
||||
"""
|
||||
Delete a website analysis.
|
||||
|
||||
Args:
|
||||
analysis_id: Analysis ID
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
analysis = self.db.query(WebsiteAnalysis).get(analysis_id)
|
||||
if analysis:
|
||||
self.db.delete(analysis)
|
||||
self.db.commit()
|
||||
logger.info(f"Deleted analysis {analysis_id}")
|
||||
return True
|
||||
return False
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"Error deleting 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.
|
||||
|
||||
Args:
|
||||
session_id: Onboarding session ID
|
||||
website_url: Website URL
|
||||
error_message: Error message
|
||||
|
||||
Returns:
|
||||
Analysis ID if successful, None otherwise
|
||||
"""
|
||||
try:
|
||||
analysis = WebsiteAnalysis(
|
||||
session_id=session_id,
|
||||
website_url=website_url,
|
||||
status='failed',
|
||||
error_message=error_message
|
||||
)
|
||||
|
||||
self.db.add(analysis)
|
||||
self.db.commit()
|
||||
logger.info(f"Saved error analysis for URL: {website_url}")
|
||||
return analysis.id
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"Error saving error analysis: {str(e)}")
|
||||
return None
|
||||
Reference in New Issue
Block a user