584 lines
26 KiB
Python
584 lines
26 KiB
Python
"""
|
|
Persona Analysis Service
|
|
Uses Gemini structured responses to analyze onboarding data and create writing personas.
|
|
|
|
NOTE: This service uses the legacy WritingPersona/PlatformPersona models.
|
|
For new code, use PersonaDataService instead, which works with the PersonaData table
|
|
and provides richer persona data from onboarding.
|
|
|
|
DEPRECATED: Consider migrating to PersonaDataService for better data richness.
|
|
"""
|
|
|
|
from typing import Dict, Any, List, Optional
|
|
from sqlalchemy.orm import Session
|
|
from loguru import logger
|
|
from datetime import datetime
|
|
import json
|
|
|
|
from services.database import get_db_session
|
|
from models.onboarding import OnboardingSession, WebsiteAnalysis, ResearchPreferences
|
|
from models.persona_models import WritingPersona, PlatformPersona, PersonaAnalysisResult
|
|
from services.persona.core_persona import CorePersonaService, OnboardingDataCollector
|
|
from services.persona.linkedin.linkedin_persona_service import LinkedInPersonaService
|
|
from services.persona.facebook.facebook_persona_service import FacebookPersonaService
|
|
|
|
class PersonaAnalysisService:
|
|
"""Service for analyzing onboarding data and generating writing personas using Gemini AI."""
|
|
|
|
_instance = None
|
|
_initialized = False
|
|
|
|
def __new__(cls):
|
|
"""Implement singleton pattern to prevent multiple initializations."""
|
|
if cls._instance is None:
|
|
cls._instance = super(PersonaAnalysisService, cls).__new__(cls)
|
|
return cls._instance
|
|
|
|
def __init__(self):
|
|
"""Initialize the persona analysis service (only once)."""
|
|
if not self._initialized:
|
|
self.core_persona_service = CorePersonaService()
|
|
self.data_collector = OnboardingDataCollector()
|
|
self.linkedin_service = LinkedInPersonaService()
|
|
self.facebook_service = FacebookPersonaService()
|
|
logger.debug("PersonaAnalysisService initialized")
|
|
self._initialized = True
|
|
|
|
def generate_persona_from_onboarding(self, user_id: int, onboarding_session_id: int = None) -> Dict[str, Any]:
|
|
"""
|
|
Generate a comprehensive writing persona from user's onboarding data.
|
|
|
|
Args:
|
|
user_id: User ID to generate persona for
|
|
onboarding_session_id: Optional specific onboarding session ID
|
|
|
|
Returns:
|
|
Generated persona data with platform adaptations
|
|
"""
|
|
try:
|
|
logger.info(f"Generating persona for user {user_id}")
|
|
|
|
# Get onboarding data
|
|
onboarding_data = self.data_collector.collect_onboarding_data(user_id, onboarding_session_id)
|
|
|
|
if not onboarding_data:
|
|
logger.warning(f"No onboarding data found for user {user_id}")
|
|
return {"error": "No onboarding data available for persona generation"}
|
|
|
|
# Generate core persona using Gemini
|
|
core_persona = self.core_persona_service.generate_core_persona(onboarding_data)
|
|
|
|
if "error" in core_persona:
|
|
return core_persona
|
|
|
|
# Generate platform-specific adaptations
|
|
platform_personas = self.core_persona_service.generate_platform_adaptations(core_persona, onboarding_data)
|
|
|
|
# Save to database
|
|
saved_persona = self._save_persona_to_db(user_id, core_persona, platform_personas, onboarding_data)
|
|
|
|
return {
|
|
"persona_id": saved_persona.id,
|
|
"core_persona": core_persona,
|
|
"platform_personas": platform_personas,
|
|
"analysis_metadata": {
|
|
"confidence_score": core_persona.get("confidence_score", 0.0),
|
|
"data_sufficiency": self.data_collector.calculate_data_sufficiency(onboarding_data),
|
|
"generated_at": datetime.utcnow().isoformat()
|
|
}
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error generating persona for user {user_id}: {str(e)}")
|
|
return {"error": f"Failed to generate persona: {str(e)}"}
|
|
|
|
|
|
def _build_persona_analysis_prompt(self, onboarding_data: Dict[str, Any]) -> str:
|
|
"""Build the main persona analysis prompt with comprehensive data."""
|
|
|
|
# Get enhanced analysis data
|
|
enhanced_analysis = onboarding_data.get("enhanced_analysis", {})
|
|
website_analysis = onboarding_data.get("website_analysis", {}) or {}
|
|
research_prefs = onboarding_data.get("research_preferences", {}) or {}
|
|
|
|
prompt = f"""
|
|
COMPREHENSIVE PERSONA GENERATION TASK: Create a highly detailed, data-driven writing persona based on extensive AI analysis of user's website and content strategy.
|
|
|
|
=== COMPREHENSIVE ONBOARDING DATA ANALYSIS ===
|
|
|
|
WEBSITE ANALYSIS OVERVIEW:
|
|
- URL: {website_analysis.get('website_url', 'Not provided')}
|
|
- Analysis Date: {website_analysis.get('analysis_date', 'Not provided')}
|
|
- Status: {website_analysis.get('status', 'Not provided')}
|
|
|
|
=== DETAILED STYLE ANALYSIS ===
|
|
{json.dumps(enhanced_analysis.get('comprehensive_style_analysis', {}), indent=2)}
|
|
|
|
=== CONTENT INSIGHTS ===
|
|
{json.dumps(enhanced_analysis.get('content_insights', {}), indent=2)}
|
|
|
|
=== AUDIENCE INTELLIGENCE ===
|
|
{json.dumps(enhanced_analysis.get('audience_intelligence', {}), indent=2)}
|
|
|
|
=== BRAND VOICE ANALYSIS ===
|
|
{json.dumps(enhanced_analysis.get('brand_voice_analysis', {}), indent=2)}
|
|
|
|
=== TECHNICAL WRITING METRICS ===
|
|
{json.dumps(enhanced_analysis.get('technical_writing_metrics', {}), indent=2)}
|
|
|
|
=== COMPETITIVE ANALYSIS ===
|
|
{json.dumps(enhanced_analysis.get('competitive_analysis', {}), indent=2)}
|
|
|
|
=== CONTENT STRATEGY INSIGHTS ===
|
|
{json.dumps(enhanced_analysis.get('content_strategy_insights', {}), indent=2)}
|
|
|
|
=== RESEARCH PREFERENCES ===
|
|
{json.dumps(enhanced_analysis.get('research_preferences', {}), indent=2)}
|
|
|
|
=== LEGACY DATA (for compatibility) ===
|
|
Website Analysis: {json.dumps(website_analysis.get('writing_style', {}), indent=2)}
|
|
Content Characteristics: {json.dumps(website_analysis.get('content_characteristics', {}) or {}, indent=2)}
|
|
Target Audience: {json.dumps(website_analysis.get('target_audience', {}), indent=2)}
|
|
Style Patterns: {json.dumps(website_analysis.get('style_patterns', {}), indent=2)}
|
|
|
|
=== COMPREHENSIVE PERSONA GENERATION REQUIREMENTS ===
|
|
|
|
1. IDENTITY CREATION (Based on Brand Analysis):
|
|
- Create a memorable persona name that captures the essence of the brand personality and writing style
|
|
- Define a clear archetype that reflects the brand's positioning and audience appeal
|
|
- Articulate a core belief that drives the writing philosophy and brand values
|
|
- Write a comprehensive brand voice description incorporating all style elements
|
|
|
|
2. LINGUISTIC FINGERPRINT (Quantitative Analysis from Technical Metrics):
|
|
- Calculate precise average sentence length from sentence structure analysis
|
|
- Determine preferred sentence types based on paragraph organization patterns
|
|
- Analyze active vs passive voice ratio from voice characteristics
|
|
- Extract go-to words and phrases from vocabulary patterns and style analysis
|
|
- List words and phrases to avoid based on brand alignment guidelines
|
|
- Determine contraction usage patterns from formality level
|
|
- Assess vocabulary complexity level from readability scores
|
|
|
|
3. RHETORICAL ANALYSIS (From Style Patterns):
|
|
- Identify metaphor patterns and themes from rhetorical devices
|
|
- Analyze analogy usage from content strategy insights
|
|
- Assess rhetorical question frequency from engagement tips
|
|
- Determine storytelling approach from content flow analysis
|
|
|
|
4. TONAL RANGE (From Comprehensive Style Analysis):
|
|
- Define the default tone from tone analysis and brand personality
|
|
- List permissible tones based on emotional appeal and audience considerations
|
|
- Identify forbidden tones from avoid elements and brand alignment
|
|
- Describe emotional range from psychographic profile and engagement level
|
|
|
|
5. STYLISTIC CONSTRAINTS (From Technical Writing Metrics):
|
|
- Define punctuation preferences from paragraph structure analysis
|
|
- Set formatting guidelines from content structure insights
|
|
- Establish paragraph structure preferences from organization patterns
|
|
- Include transition phrase preferences from style patterns
|
|
|
|
6. PLATFORM-SPECIFIC ADAPTATIONS (From Content Strategy):
|
|
- Incorporate SEO optimization strategies
|
|
- Include conversion optimization techniques
|
|
- Apply engagement tips for different platforms
|
|
- Use competitive advantages for differentiation
|
|
|
|
7. CONTENT STRATEGY INTEGRATION:
|
|
- Incorporate best practices from content strategy insights
|
|
- Include AI generation tips for consistent output
|
|
- Apply content calendar suggestions for timing
|
|
- Use competitive advantages for positioning
|
|
|
|
=== ENHANCED ANALYSIS INSTRUCTIONS ===
|
|
- Base your analysis on ALL the comprehensive data provided above
|
|
- Use the detailed technical metrics for precise linguistic analysis
|
|
- Incorporate brand voice analysis for authentic personality
|
|
- Apply audience intelligence for targeted communication
|
|
- Include competitive analysis for market positioning
|
|
- Use content strategy insights for practical application
|
|
- Ensure the persona reflects the brand's unique elements and competitive advantages
|
|
- Provide a confidence score (0-100) based on data richness and quality
|
|
- Include detailed analysis notes explaining your reasoning and data sources
|
|
|
|
Generate a comprehensive, data-driven persona profile that can be used to replicate this writing style across different platforms while maintaining brand authenticity and competitive positioning.
|
|
"""
|
|
|
|
return prompt
|
|
|
|
def _build_platform_adaptation_prompt(self, core_persona: Dict[str, Any], platform: str, onboarding_data: Dict[str, Any]) -> str:
|
|
"""Build prompt for platform-specific persona adaptation."""
|
|
|
|
platform_constraints = self._get_platform_constraints(platform)
|
|
|
|
prompt = f"""
|
|
PLATFORM ADAPTATION TASK: Adapt the core writing persona for {platform.upper()}.
|
|
|
|
CORE PERSONA:
|
|
{json.dumps(core_persona, indent=2)}
|
|
|
|
PLATFORM: {platform.upper()}
|
|
|
|
PLATFORM CONSTRAINTS:
|
|
{json.dumps(platform_constraints, indent=2)}
|
|
|
|
ADAPTATION REQUIREMENTS:
|
|
|
|
1. SENTENCE METRICS:
|
|
- Adjust sentence length for platform optimal performance
|
|
- Adapt sentence variety for platform engagement
|
|
- Consider platform reading patterns
|
|
|
|
2. LEXICAL ADAPTATIONS:
|
|
- Identify platform-specific vocabulary and slang
|
|
- Define hashtag strategy (if applicable)
|
|
- Set emoji usage guidelines
|
|
- Establish mention and tagging strategy
|
|
|
|
3. CONTENT FORMAT RULES:
|
|
- Respect character/word limits
|
|
- Optimize paragraph structure for platform
|
|
- Define call-to-action style
|
|
- Set link placement strategy
|
|
|
|
4. ENGAGEMENT PATTERNS:
|
|
- Determine optimal posting frequency
|
|
- Identify best posting times for audience
|
|
- Define engagement tactics
|
|
- Set community interaction guidelines
|
|
|
|
5. PLATFORM BEST PRACTICES:
|
|
- List platform-specific optimization techniques
|
|
- Consider algorithm preferences
|
|
- Include trending format adaptations
|
|
|
|
INSTRUCTIONS:
|
|
- Maintain the core persona identity while optimizing for platform performance
|
|
- Ensure all adaptations align with the original brand voice
|
|
- Consider platform-specific audience behavior
|
|
- Provide actionable, specific guidelines
|
|
|
|
Generate a platform-optimized persona adaptation that maintains brand consistency while maximizing platform performance.
|
|
"""
|
|
|
|
return prompt
|
|
|
|
|
|
def _get_platform_constraints(self, platform: str) -> Dict[str, Any]:
|
|
"""Get platform-specific constraints and best practices."""
|
|
|
|
constraints = {
|
|
"twitter": {
|
|
"character_limit": 280,
|
|
"optimal_length": "120-150 characters",
|
|
"hashtag_limit": 3,
|
|
"image_support": True,
|
|
"thread_support": True,
|
|
"link_shortening": True
|
|
},
|
|
"linkedin": self.linkedin_service.get_linkedin_constraints(),
|
|
"facebook": self.facebook_service.get_facebook_constraints(),
|
|
"instagram": {
|
|
"caption_limit": 2200,
|
|
"optimal_length": "125-150 words",
|
|
"hashtag_limit": 30,
|
|
"visual_first": True,
|
|
"story_support": True,
|
|
"emoji_friendly": True
|
|
},
|
|
"facebook": {
|
|
"character_limit": 63206,
|
|
"optimal_length": "40-80 words",
|
|
"algorithm_favors": "engagement",
|
|
"link_preview": True,
|
|
"event_support": True,
|
|
"group_sharing": True
|
|
},
|
|
"blog": {
|
|
"word_count": "800-2000 words",
|
|
"seo_important": True,
|
|
"header_structure": True,
|
|
"internal_linking": True,
|
|
"meta_descriptions": True,
|
|
"readability_score": True
|
|
},
|
|
"medium": {
|
|
"word_count": "1000-3000 words",
|
|
"storytelling_focus": True,
|
|
"subtitle_support": True,
|
|
"publication_support": True,
|
|
"clap_optimization": True,
|
|
"follower_building": True
|
|
},
|
|
"substack": {
|
|
"newsletter_format": True,
|
|
"email_optimization": True,
|
|
"subscription_focus": True,
|
|
"long_form": True,
|
|
"personal_connection": True,
|
|
"monetization_support": True
|
|
}
|
|
}
|
|
|
|
return constraints.get(platform, {})
|
|
|
|
def _save_persona_to_db(self, user_id: int, core_persona: Dict[str, Any], platform_personas: Dict[str, Any], onboarding_data: Dict[str, Any]) -> WritingPersona:
|
|
"""Save generated persona to database."""
|
|
try:
|
|
session = get_db_session()
|
|
|
|
# Create main persona record
|
|
writing_persona = WritingPersona(
|
|
user_id=user_id,
|
|
persona_name=core_persona.get("identity", {}).get("persona_name", "Generated Persona"),
|
|
archetype=core_persona.get("identity", {}).get("archetype"),
|
|
core_belief=core_persona.get("identity", {}).get("core_belief"),
|
|
brand_voice_description=core_persona.get("identity", {}).get("brand_voice_description"),
|
|
linguistic_fingerprint=core_persona.get("linguistic_fingerprint", {}),
|
|
platform_adaptations={"platforms": list(platform_personas.keys())},
|
|
onboarding_session_id=onboarding_data.get("session_info", {}).get("session_id"),
|
|
source_website_analysis=onboarding_data.get("website_analysis") or {},
|
|
source_research_preferences=onboarding_data.get("research_preferences") or {},
|
|
ai_analysis_version="gemini_v1.0",
|
|
confidence_score=core_persona.get("confidence_score", 0.0)
|
|
)
|
|
|
|
session.add(writing_persona)
|
|
session.commit()
|
|
session.refresh(writing_persona)
|
|
|
|
# Create platform-specific persona records
|
|
for platform, platform_data in platform_personas.items():
|
|
# Prepare platform-specific data
|
|
platform_specific_data = {}
|
|
if platform.lower() == "linkedin":
|
|
platform_specific_data = {
|
|
"professional_networking": platform_data.get("professional_networking", {}),
|
|
"linkedin_features": platform_data.get("linkedin_features", {}),
|
|
"algorithm_optimization": platform_data.get("algorithm_optimization", {}),
|
|
"professional_context_optimization": platform_data.get("professional_context_optimization", {})
|
|
}
|
|
elif platform.lower() == "facebook":
|
|
platform_specific_data = {
|
|
"facebook_algorithm_optimization": platform_data.get("facebook_algorithm_optimization", {}),
|
|
"facebook_engagement_strategies": platform_data.get("facebook_engagement_strategies", {}),
|
|
"facebook_content_formats": platform_data.get("facebook_content_formats", {}),
|
|
"facebook_audience_targeting": platform_data.get("facebook_audience_targeting", {}),
|
|
"facebook_community_building": platform_data.get("facebook_community_building", {})
|
|
}
|
|
|
|
platform_persona = PlatformPersona(
|
|
writing_persona_id=writing_persona.id,
|
|
platform_type=platform,
|
|
sentence_metrics=platform_data.get("sentence_metrics", {}),
|
|
lexical_features=platform_data.get("lexical_adaptations", {}),
|
|
rhetorical_devices=core_persona.get("linguistic_fingerprint", {}).get("rhetorical_devices", {}),
|
|
tonal_range=core_persona.get("tonal_range", {}),
|
|
stylistic_constraints=core_persona.get("stylistic_constraints", {}),
|
|
content_format_rules=platform_data.get("content_format_rules", {}),
|
|
engagement_patterns=platform_data.get("engagement_patterns", {}),
|
|
platform_best_practices={"practices": platform_data.get("platform_best_practices", [])},
|
|
algorithm_considerations=platform_specific_data if platform_specific_data else platform_data.get("algorithm_considerations", {})
|
|
)
|
|
session.add(platform_persona)
|
|
|
|
# Save analysis result
|
|
analysis_result = PersonaAnalysisResult(
|
|
user_id=user_id,
|
|
writing_persona_id=writing_persona.id,
|
|
analysis_prompt=self._build_persona_analysis_prompt(onboarding_data)[:5000], # Truncate for storage
|
|
input_data=onboarding_data,
|
|
linguistic_analysis=core_persona.get("linguistic_fingerprint", {}),
|
|
personality_analysis=core_persona.get("identity", {}),
|
|
platform_recommendations=platform_personas,
|
|
style_guidelines=core_persona.get("stylistic_constraints", {}),
|
|
analysis_confidence=core_persona.get("confidence_score", 0.0),
|
|
data_sufficiency_score=self._calculate_data_sufficiency(onboarding_data),
|
|
ai_provider="gemini",
|
|
model_version="gemini-2.5-flash"
|
|
)
|
|
session.add(analysis_result)
|
|
|
|
session.commit()
|
|
persona_id = writing_persona.id
|
|
session.close()
|
|
|
|
logger.info(f"✅ Persona saved to database with ID: {persona_id}")
|
|
return writing_persona
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error saving persona to database: {str(e)}")
|
|
if session:
|
|
session.rollback()
|
|
session.close()
|
|
raise
|
|
|
|
def _calculate_data_sufficiency(self, onboarding_data: Dict[str, Any]) -> float:
|
|
"""Calculate how sufficient the onboarding data is for persona generation."""
|
|
score = 0.0
|
|
|
|
# Get enhanced analysis data
|
|
enhanced_analysis = onboarding_data.get("enhanced_analysis", {})
|
|
website_analysis = onboarding_data.get("website_analysis", {}) or {}
|
|
research_prefs = onboarding_data.get("research_preferences", {}) or {}
|
|
|
|
# Enhanced scoring based on comprehensive data availability
|
|
|
|
# Comprehensive Style Analysis (25% of score)
|
|
style_analysis = enhanced_analysis.get("comprehensive_style_analysis", {})
|
|
if style_analysis.get("tone_analysis"):
|
|
score += 5
|
|
if style_analysis.get("voice_characteristics"):
|
|
score += 5
|
|
if style_analysis.get("brand_personality"):
|
|
score += 5
|
|
if style_analysis.get("formality_level"):
|
|
score += 5
|
|
if style_analysis.get("emotional_appeal"):
|
|
score += 5
|
|
|
|
# Content Insights (20% of score)
|
|
content_insights = enhanced_analysis.get("content_insights", {})
|
|
if content_insights.get("sentence_structure_analysis"):
|
|
score += 4
|
|
if content_insights.get("vocabulary_level"):
|
|
score += 4
|
|
if content_insights.get("readability_score"):
|
|
score += 4
|
|
if content_insights.get("content_flow"):
|
|
score += 4
|
|
if content_insights.get("visual_elements_usage"):
|
|
score += 4
|
|
|
|
# Audience Intelligence (15% of score)
|
|
audience_intel = enhanced_analysis.get("audience_intelligence", {})
|
|
if audience_intel.get("demographics"):
|
|
score += 3
|
|
if audience_intel.get("expertise_level"):
|
|
score += 3
|
|
if audience_intel.get("industry_focus"):
|
|
score += 3
|
|
if audience_intel.get("psychographic_profile"):
|
|
score += 3
|
|
if audience_intel.get("pain_points"):
|
|
score += 3
|
|
|
|
# Technical Writing Metrics (15% of score)
|
|
tech_metrics = enhanced_analysis.get("technical_writing_metrics", {})
|
|
if tech_metrics.get("vocabulary_patterns"):
|
|
score += 3
|
|
if tech_metrics.get("rhetorical_devices"):
|
|
score += 3
|
|
if tech_metrics.get("paragraph_structure"):
|
|
score += 3
|
|
if tech_metrics.get("style_consistency"):
|
|
score += 3
|
|
if tech_metrics.get("unique_elements"):
|
|
score += 3
|
|
|
|
# Content Strategy Insights (15% of score)
|
|
strategy_insights = enhanced_analysis.get("content_strategy_insights", {})
|
|
if strategy_insights.get("tone_recommendations"):
|
|
score += 3
|
|
if strategy_insights.get("best_practices"):
|
|
score += 3
|
|
if strategy_insights.get("competitive_advantages"):
|
|
score += 3
|
|
if strategy_insights.get("content_strategy"):
|
|
score += 3
|
|
if strategy_insights.get("ai_generation_tips"):
|
|
score += 3
|
|
|
|
# Research Preferences (10% of score)
|
|
if research_prefs.get("research_depth"):
|
|
score += 5
|
|
if research_prefs.get("content_types"):
|
|
score += 5
|
|
|
|
# Legacy compatibility - add points for basic data if enhanced data is missing
|
|
if score < 50: # If enhanced data is insufficient, fall back to legacy scoring
|
|
legacy_score = 0.0
|
|
|
|
# Website analysis components (70% of legacy score)
|
|
if website_analysis.get("writing_style"):
|
|
legacy_score += 25
|
|
if website_analysis.get("content_characteristics"):
|
|
legacy_score += 20
|
|
if website_analysis.get("target_audience"):
|
|
legacy_score += 15
|
|
if website_analysis.get("style_patterns"):
|
|
legacy_score += 10
|
|
|
|
# Research preferences components (30% of legacy score)
|
|
if research_prefs.get("research_depth"):
|
|
legacy_score += 10
|
|
if research_prefs.get("content_types"):
|
|
legacy_score += 10
|
|
if research_prefs.get("writing_style"):
|
|
legacy_score += 10
|
|
|
|
# Use the higher of enhanced or legacy score
|
|
score = max(score, legacy_score)
|
|
|
|
return min(score, 100.0)
|
|
|
|
def get_user_personas(self, user_id: str) -> List[Dict[str, Any]]:
|
|
"""Get all personas for a user."""
|
|
try:
|
|
session = get_db_session()
|
|
|
|
personas = session.query(WritingPersona).filter(
|
|
WritingPersona.user_id == user_id,
|
|
WritingPersona.is_active == True
|
|
).all()
|
|
|
|
result = []
|
|
for persona in personas:
|
|
persona_dict = persona.to_dict()
|
|
|
|
# Get platform personas
|
|
platform_personas = session.query(PlatformPersona).filter(
|
|
PlatformPersona.writing_persona_id == persona.id,
|
|
PlatformPersona.is_active == True
|
|
).all()
|
|
|
|
persona_dict["platforms"] = [pp.to_dict() for pp in platform_personas]
|
|
result.append(persona_dict)
|
|
|
|
session.close()
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting user personas: {str(e)}")
|
|
return []
|
|
|
|
def get_persona_for_platform(self, user_id: str, platform: str) -> Optional[Dict[str, Any]]:
|
|
"""Get the best persona for a specific platform."""
|
|
try:
|
|
session = get_db_session()
|
|
|
|
# Get the most recent active persona
|
|
persona = session.query(WritingPersona).filter(
|
|
WritingPersona.user_id == user_id,
|
|
WritingPersona.is_active == True
|
|
).order_by(WritingPersona.created_at.desc()).first()
|
|
|
|
if not persona:
|
|
return None
|
|
|
|
# Get platform-specific adaptation
|
|
platform_persona = session.query(PlatformPersona).filter(
|
|
PlatformPersona.writing_persona_id == persona.id,
|
|
PlatformPersona.platform_type == platform,
|
|
PlatformPersona.is_active == True
|
|
).first()
|
|
|
|
result = {
|
|
"core_persona": persona.to_dict(),
|
|
"platform_adaptation": platform_persona.to_dict() if platform_persona else None
|
|
}
|
|
|
|
session.close()
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error getting persona for platform {platform}: {str(e)}")
|
|
return None |