277 lines
11 KiB
Python
277 lines
11 KiB
Python
"""
|
|
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 |