Base code

This commit is contained in:
Kunthawat Greethong
2026-01-08 22:39:53 +07:00
parent 697115c61a
commit c35fa52117
2169 changed files with 626670 additions and 0 deletions

View 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