LinkedIn and Facebook Persona Services Implementation
This commit is contained in:
@@ -8,6 +8,7 @@ from pydantic import BaseModel, Field
|
||||
from typing import Dict, Any, List, Optional
|
||||
from datetime import datetime
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from services.persona_analysis_service import PersonaAnalysisService
|
||||
from services.database import get_db
|
||||
@@ -110,50 +111,45 @@ async def generate_persona(user_id: int, request: PersonaGenerationRequest):
|
||||
logger.error(f"Error generating persona: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to generate persona: {str(e)}")
|
||||
|
||||
async def get_user_personas(user_id: int):
|
||||
"""Get all personas for a user."""
|
||||
async def get_user_personas(user_id: str):
|
||||
"""Get all personas for a user using PersonaData."""
|
||||
try:
|
||||
persona_service = get_persona_service()
|
||||
personas = persona_service.get_user_personas(user_id)
|
||||
from services.persona_data_service import PersonaDataService
|
||||
|
||||
persona_service = PersonaDataService()
|
||||
all_personas = persona_service.get_all_platform_personas(user_id)
|
||||
|
||||
return {
|
||||
"personas": personas,
|
||||
"total_count": len(personas)
|
||||
"personas": all_personas,
|
||||
"total_count": len(all_personas),
|
||||
"platforms": list(all_personas.keys())
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting user personas: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get personas: {str(e)}")
|
||||
|
||||
async def get_persona_details(user_id: int, persona_id: int):
|
||||
"""Get detailed information about a specific persona."""
|
||||
async def get_persona_details(user_id: str, persona_id: int):
|
||||
"""Get detailed information about a specific persona using PersonaData."""
|
||||
try:
|
||||
from services.database import get_db_session
|
||||
from models.persona_models import WritingPersona, PlatformPersona
|
||||
from services.persona_data_service import PersonaDataService
|
||||
|
||||
session = get_db_session()
|
||||
persona_service = PersonaDataService()
|
||||
persona_data = persona_service.get_user_persona_data(user_id)
|
||||
|
||||
# Get persona
|
||||
persona = session.query(WritingPersona).filter(
|
||||
WritingPersona.id == persona_id,
|
||||
WritingPersona.user_id == user_id,
|
||||
WritingPersona.is_active == True
|
||||
).first()
|
||||
|
||||
if not persona:
|
||||
if not persona_data:
|
||||
raise HTTPException(status_code=404, detail="Persona not found")
|
||||
|
||||
# Get platform adaptations
|
||||
platform_personas = session.query(PlatformPersona).filter(
|
||||
PlatformPersona.writing_persona_id == persona_id,
|
||||
PlatformPersona.is_active == True
|
||||
).all()
|
||||
|
||||
result = persona.to_dict()
|
||||
result["platform_adaptations"] = [pp.to_dict() for pp in platform_personas]
|
||||
|
||||
session.close()
|
||||
return result
|
||||
# Return the complete persona data with all platforms
|
||||
return {
|
||||
"persona_id": persona_data.get('id'),
|
||||
"core_persona": persona_data.get('core_persona', {}),
|
||||
"platform_personas": persona_data.get('platform_personas', {}),
|
||||
"quality_metrics": persona_data.get('quality_metrics', {}),
|
||||
"selected_platforms": persona_data.get('selected_platforms', []),
|
||||
"created_at": persona_data.get('created_at'),
|
||||
"updated_at": persona_data.get('updated_at')
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
@@ -161,11 +157,13 @@ async def get_persona_details(user_id: int, persona_id: int):
|
||||
logger.error(f"Error getting persona details: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get persona details: {str(e)}")
|
||||
|
||||
async def get_platform_persona(user_id: int, platform: str):
|
||||
"""Get persona adaptation for a specific platform."""
|
||||
async def get_platform_persona(user_id: str, platform: str):
|
||||
"""Get persona adaptation for a specific platform using PersonaData."""
|
||||
try:
|
||||
persona_service = get_persona_service()
|
||||
platform_persona = persona_service.get_persona_for_platform(user_id, platform)
|
||||
from services.persona_data_service import PersonaDataService
|
||||
|
||||
persona_service = PersonaDataService()
|
||||
platform_persona = persona_service.get_platform_persona(user_id, platform)
|
||||
|
||||
if not platform_persona:
|
||||
raise HTTPException(status_code=404, detail=f"No persona found for platform {platform}")
|
||||
@@ -178,41 +176,52 @@ async def get_platform_persona(user_id: int, platform: str):
|
||||
logger.error(f"Error getting platform persona: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get platform persona: {str(e)}")
|
||||
|
||||
async def update_persona(user_id: int, persona_id: int, update_data: Dict[str, Any]):
|
||||
"""Update an existing persona."""
|
||||
async def get_persona_summary(user_id: str):
|
||||
"""Get persona summary for a user using PersonaData."""
|
||||
try:
|
||||
from services.database import get_db_session
|
||||
from models.persona_models import WritingPersona
|
||||
from services.persona_data_service import PersonaDataService
|
||||
|
||||
session = get_db_session()
|
||||
persona_service = PersonaDataService()
|
||||
summary = persona_service.get_persona_summary(user_id)
|
||||
|
||||
persona = session.query(WritingPersona).filter(
|
||||
WritingPersona.id == persona_id,
|
||||
WritingPersona.user_id == user_id
|
||||
).first()
|
||||
return summary
|
||||
|
||||
if not persona:
|
||||
raise HTTPException(status_code=404, detail="Persona not found")
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting persona summary: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get persona summary: {str(e)}")
|
||||
|
||||
async def update_persona(user_id: str, persona_id: int, update_data: Dict[str, Any]):
|
||||
"""Update an existing persona using PersonaData."""
|
||||
try:
|
||||
from services.persona_data_service import PersonaDataService
|
||||
from models.onboarding import PersonaData
|
||||
|
||||
# Update allowed fields
|
||||
updatable_fields = [
|
||||
'persona_name', 'archetype', 'core_belief', 'brand_voice_description',
|
||||
'linguistic_fingerprint', 'platform_adaptations'
|
||||
]
|
||||
persona_service = PersonaDataService()
|
||||
|
||||
for field in updatable_fields:
|
||||
if field in update_data:
|
||||
setattr(persona, field, update_data[field])
|
||||
|
||||
persona.updated_at = datetime.utcnow()
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
return {
|
||||
"message": "Persona updated successfully",
|
||||
"persona_id": persona_id,
|
||||
"updated_at": persona.updated_at.isoformat()
|
||||
}
|
||||
# For PersonaData, we update the core_persona field
|
||||
if 'core_persona' in update_data:
|
||||
# Get current persona data
|
||||
persona_data = persona_service.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
raise HTTPException(status_code=404, detail="Persona not found")
|
||||
|
||||
# Update core persona with new data
|
||||
persona_service.db.query(PersonaData).filter(
|
||||
PersonaData.id == persona_data.get('id')
|
||||
).update({
|
||||
'core_persona': update_data['core_persona'],
|
||||
'updated_at': datetime.utcnow()
|
||||
})
|
||||
persona_service.db.commit()
|
||||
persona_service.db.close()
|
||||
|
||||
return {
|
||||
"message": "Persona updated successfully",
|
||||
"persona_id": persona_data.get('id'),
|
||||
"updated_at": datetime.utcnow().isoformat()
|
||||
}
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="core_persona field is required for updates")
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
@@ -220,40 +229,28 @@ async def update_persona(user_id: int, persona_id: int, update_data: Dict[str, A
|
||||
logger.error(f"Error updating persona: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to update persona: {str(e)}")
|
||||
|
||||
async def delete_persona(user_id: int, persona_id: int):
|
||||
"""Delete a persona (soft delete by setting is_active=False)."""
|
||||
async def delete_persona(user_id: str, persona_id: int):
|
||||
"""Delete a persona using PersonaData (not recommended, personas are generated during onboarding)."""
|
||||
try:
|
||||
from services.database import get_db_session
|
||||
from models.persona_models import WritingPersona, PlatformPersona
|
||||
from services.persona_data_service import PersonaDataService
|
||||
from models.onboarding import PersonaData
|
||||
|
||||
session = get_db_session()
|
||||
persona_service = PersonaDataService()
|
||||
|
||||
persona = session.query(WritingPersona).filter(
|
||||
WritingPersona.id == persona_id,
|
||||
WritingPersona.user_id == user_id
|
||||
).first()
|
||||
|
||||
if not persona:
|
||||
# Get persona data
|
||||
persona_data = persona_service.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
raise HTTPException(status_code=404, detail="Persona not found")
|
||||
|
||||
# Soft delete persona and platform adaptations
|
||||
persona.is_active = False
|
||||
persona.updated_at = datetime.utcnow()
|
||||
|
||||
platform_personas = session.query(PlatformPersona).filter(
|
||||
PlatformPersona.writing_persona_id == persona_id
|
||||
).all()
|
||||
|
||||
for pp in platform_personas:
|
||||
pp.is_active = False
|
||||
pp.updated_at = datetime.utcnow()
|
||||
|
||||
session.commit()
|
||||
session.close()
|
||||
# For PersonaData, we mark it as deleted by setting a flag
|
||||
# Note: In production, you might want to add a deleted_at field or similar
|
||||
# For now, we'll just return a warning that deletion is not recommended
|
||||
logger.warning(f"Delete persona requested for user {user_id}. PersonaData deletion is not recommended.")
|
||||
|
||||
return {
|
||||
"message": "Persona deleted successfully",
|
||||
"persona_id": persona_id
|
||||
"message": "Persona deletion requested. Note: Personas are generated during onboarding and deletion is not recommended.",
|
||||
"persona_id": persona_data.get('id'),
|
||||
"alternative": "Consider re-running onboarding to regenerate persona if needed."
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
@@ -262,67 +259,24 @@ async def delete_persona(user_id: int, persona_id: int):
|
||||
logger.error(f"Error deleting persona: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to delete persona: {str(e)}")
|
||||
|
||||
async def update_platform_persona(user_id: int, platform: str, update_data: Dict[str, Any]):
|
||||
"""Update platform-specific persona fields for a user's persona.
|
||||
|
||||
This updates the underlying PlatformPersona row for the given platform.
|
||||
"""
|
||||
async def update_platform_persona(user_id: str, platform: str, update_data: Dict[str, Any]):
|
||||
"""Update platform-specific persona fields using PersonaData."""
|
||||
try:
|
||||
from services.database import get_db_session
|
||||
from models.persona_models import WritingPersona, PlatformPersona
|
||||
from services.persona_data_service import PersonaDataService
|
||||
|
||||
session = get_db_session()
|
||||
|
||||
# Find the user's active core persona id
|
||||
core_persona = session.query(WritingPersona).filter(
|
||||
WritingPersona.user_id == user_id,
|
||||
WritingPersona.is_active == True
|
||||
).order_by(WritingPersona.created_at.desc()).first()
|
||||
|
||||
if not core_persona:
|
||||
raise HTTPException(status_code=404, detail="No active persona found for user")
|
||||
|
||||
# Find the platform persona for the requested platform
|
||||
platform_persona = session.query(PlatformPersona).filter(
|
||||
PlatformPersona.writing_persona_id == core_persona.id,
|
||||
PlatformPersona.platform_type.ilike(platform),
|
||||
PlatformPersona.is_active == True
|
||||
).first()
|
||||
|
||||
if not platform_persona:
|
||||
persona_service = PersonaDataService()
|
||||
|
||||
# Update platform-specific persona data
|
||||
success = persona_service.update_platform_persona(user_id, platform, update_data)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail=f"No platform persona found for platform {platform}")
|
||||
|
||||
# Update allowed platform fields
|
||||
updatable_fields = [
|
||||
'sentence_metrics', 'lexical_features', 'rhetorical_devices', 'tonal_range',
|
||||
'stylistic_constraints', 'content_format_rules', 'engagement_patterns',
|
||||
'posting_frequency', 'content_types', 'platform_best_practices', 'algorithm_considerations'
|
||||
]
|
||||
|
||||
updated_any = False
|
||||
for field in updatable_fields:
|
||||
if field in update_data:
|
||||
setattr(platform_persona, field, update_data[field])
|
||||
updated_any = True
|
||||
|
||||
if not updated_any:
|
||||
# Nothing to update
|
||||
session.close()
|
||||
return {
|
||||
"message": "No updatable fields provided",
|
||||
"platform": platform_persona.platform_type,
|
||||
"persona_id": core_persona.id
|
||||
}
|
||||
|
||||
platform_persona.updated_at = datetime.utcnow()
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
return {
|
||||
"message": "Platform persona updated successfully",
|
||||
"platform": platform,
|
||||
"persona_id": core_persona.id,
|
||||
"updated_at": platform_persona.updated_at.isoformat()
|
||||
"user_id": user_id,
|
||||
"updated_at": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
@@ -331,6 +285,101 @@ async def update_platform_persona(user_id: int, platform: str, update_data: Dict
|
||||
logger.error(f"Error updating platform persona: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to update platform persona: {str(e)}")
|
||||
|
||||
async def generate_platform_persona(user_id: str, platform: str, db_session):
|
||||
"""
|
||||
Generate a platform-specific persona from core persona and save it.
|
||||
|
||||
Args:
|
||||
user_id: User ID from auth
|
||||
platform: Platform name (facebook, linkedin, etc.)
|
||||
db_session: Database session from FastAPI dependency injection
|
||||
|
||||
Returns:
|
||||
Generated platform persona with validation results
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Generating {platform} persona for user {user_id}")
|
||||
|
||||
# Import services
|
||||
from services.persona_data_service import PersonaDataService
|
||||
from services.onboarding_database_service import OnboardingDatabaseService
|
||||
|
||||
persona_data_service = PersonaDataService(db_session=db_session)
|
||||
onboarding_service = OnboardingDatabaseService(db=db_session)
|
||||
|
||||
# Get core persona data
|
||||
persona_data = persona_data_service.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
raise HTTPException(status_code=404, detail="Core persona not found")
|
||||
|
||||
core_persona = persona_data.get('core_persona', {})
|
||||
if not core_persona:
|
||||
raise HTTPException(status_code=404, detail="Core persona data is empty")
|
||||
|
||||
# Get onboarding data for context
|
||||
onboarding_session = onboarding_service.get_session_by_user(user_id)
|
||||
if not onboarding_session:
|
||||
raise HTTPException(status_code=404, detail="Onboarding session not found")
|
||||
|
||||
# Get website analysis for context
|
||||
website_analysis = onboarding_service.get_website_analysis(user_id)
|
||||
research_prefs = onboarding_service.get_research_preferences(user_id)
|
||||
|
||||
onboarding_data = {
|
||||
"website_url": website_analysis.get('website_url', '') if website_analysis else '',
|
||||
"writing_style": website_analysis.get('writing_style', {}) if website_analysis else {},
|
||||
"content_characteristics": website_analysis.get('content_characteristics', {}) if website_analysis else {},
|
||||
"target_audience": website_analysis.get('target_audience', '') if website_analysis else '',
|
||||
"research_preferences": research_prefs or {}
|
||||
}
|
||||
|
||||
# Generate platform persona based on platform
|
||||
generated_persona = None
|
||||
platform_service = None
|
||||
|
||||
if platform.lower() == 'facebook':
|
||||
from services.persona.facebook.facebook_persona_service import FacebookPersonaService
|
||||
platform_service = FacebookPersonaService()
|
||||
generated_persona = platform_service.generate_facebook_persona(
|
||||
core_persona,
|
||||
onboarding_data
|
||||
)
|
||||
elif platform.lower() == 'linkedin':
|
||||
from services.persona.linkedin.linkedin_persona_service import LinkedInPersonaService
|
||||
platform_service = LinkedInPersonaService()
|
||||
generated_persona = platform_service.generate_linkedin_persona(
|
||||
core_persona,
|
||||
onboarding_data
|
||||
)
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"Unsupported platform: {platform}")
|
||||
|
||||
# Check for errors in generation
|
||||
if "error" in generated_persona:
|
||||
raise HTTPException(status_code=500, detail=generated_persona["error"])
|
||||
|
||||
# Save the generated platform persona to database
|
||||
success = persona_data_service.save_platform_persona(user_id, platform, generated_persona)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to save {platform} persona")
|
||||
|
||||
logger.info(f"✅ Successfully generated and saved {platform} persona for user {user_id}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"platform": platform,
|
||||
"persona": generated_persona,
|
||||
"validation_results": generated_persona.get("validation_results", {}),
|
||||
"quality_score": generated_persona.get("validation_results", {}).get("quality_score", 0)
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating {platform} persona: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to generate {platform} persona: {str(e)}")
|
||||
|
||||
async def validate_persona_generation_readiness(user_id: int):
|
||||
"""Check if user has sufficient onboarding data for persona generation."""
|
||||
try:
|
||||
|
||||
@@ -3,14 +3,18 @@ FastAPI routes for persona management.
|
||||
Integrates persona generation and management into the main API.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from fastapi import APIRouter, HTTPException, Query, Depends
|
||||
from typing import Dict, Any, Optional
|
||||
from sqlalchemy.orm import Session
|
||||
from middleware.auth_middleware import get_current_user
|
||||
from services.database import get_db
|
||||
|
||||
from api.persona import (
|
||||
generate_persona,
|
||||
get_user_personas,
|
||||
get_persona_details,
|
||||
get_platform_persona,
|
||||
get_persona_summary,
|
||||
update_persona,
|
||||
delete_persona,
|
||||
validate_persona_generation_readiness,
|
||||
@@ -32,7 +36,7 @@ from api.persona import (
|
||||
)
|
||||
|
||||
from services.persona_replication_engine import PersonaReplicationEngine
|
||||
from api.persona import update_platform_persona
|
||||
from api.persona import update_platform_persona, generate_platform_persona
|
||||
|
||||
# Create router
|
||||
router = APIRouter(prefix="/api/personas", tags=["personas"])
|
||||
@@ -45,29 +49,45 @@ async def generate_persona_endpoint(
|
||||
"""Generate a new writing persona from onboarding data."""
|
||||
return await generate_persona(user_id, request)
|
||||
|
||||
@router.get("/user/{user_id}")
|
||||
async def get_user_personas_endpoint(user_id: int):
|
||||
"""Get all personas for a user."""
|
||||
# Beta testing: Force user_id=1 for all requests
|
||||
return await get_user_personas(1)
|
||||
@router.get("/user")
|
||||
async def get_user_personas_endpoint(current_user: Dict[str, Any] = Depends(get_current_user)):
|
||||
"""Get all personas for the current user."""
|
||||
user_id = str(current_user.get('id'))
|
||||
return await get_user_personas(user_id)
|
||||
|
||||
@router.get("/summary")
|
||||
async def get_persona_summary_endpoint(current_user: Dict[str, Any] = Depends(get_current_user)):
|
||||
"""Get persona summary for the current user."""
|
||||
user_id = str(current_user.get('id'))
|
||||
return await get_persona_summary(user_id)
|
||||
|
||||
@router.get("/{persona_id}")
|
||||
async def get_persona_details_endpoint(
|
||||
persona_id: int,
|
||||
user_id: int = Query(..., description="User ID")
|
||||
current_user: Dict[str, Any] = Depends(get_current_user)
|
||||
):
|
||||
"""Get detailed information about a specific persona."""
|
||||
# Beta testing: Force user_id=1 for all requests
|
||||
return await get_persona_details(1, persona_id)
|
||||
user_id = str(current_user.get('id'))
|
||||
return await get_persona_details(user_id, persona_id)
|
||||
|
||||
@router.get("/platform/{platform}")
|
||||
async def get_platform_persona_endpoint(
|
||||
platform: str,
|
||||
user_id: int = Query(1, description="User ID")
|
||||
current_user: Dict[str, Any] = Depends(get_current_user)
|
||||
):
|
||||
"""Get persona adaptation for a specific platform."""
|
||||
# Beta testing: Force user_id=1 for all requests
|
||||
return await get_platform_persona(1, platform)
|
||||
user_id = str(current_user.get('id'))
|
||||
return await get_platform_persona(user_id, platform)
|
||||
|
||||
@router.post("/generate-platform/{platform}")
|
||||
async def generate_platform_persona_endpoint(
|
||||
platform: str,
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Generate a platform-specific persona from core persona."""
|
||||
user_id = str(current_user.get('id'))
|
||||
return await generate_platform_persona(user_id, platform, db)
|
||||
|
||||
@router.put("/{persona_id}")
|
||||
async def update_persona_endpoint(
|
||||
|
||||
@@ -18,7 +18,7 @@ class WritingPersona(Base):
|
||||
|
||||
# Primary fields
|
||||
id = Column(Integer, primary_key=True)
|
||||
user_id = Column(Integer, nullable=False)
|
||||
user_id = Column(String(255), nullable=False) # Changed to String to support Clerk user IDs
|
||||
persona_name = Column(String(255), nullable=False) # e.g., "Professional LinkedIn Voice", "Casual Blog Writer"
|
||||
|
||||
# Core Identity
|
||||
|
||||
@@ -355,19 +355,36 @@ class FacebookPersonaService:
|
||||
"properties": {
|
||||
"text_posts": {
|
||||
"type": "object",
|
||||
"description": "Text post optimization for Facebook"
|
||||
"description": "Text post optimization for Facebook",
|
||||
"properties": {
|
||||
"optimal_length": {"type": "string"},
|
||||
"structure_guidelines": {"type": "array", "items": {"type": "string"}},
|
||||
"hook_strategies": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
},
|
||||
"image_posts": {
|
||||
"type": "object",
|
||||
"description": "Image post optimization for Facebook"
|
||||
"description": "Image post optimization for Facebook",
|
||||
"properties": {
|
||||
"image_guidelines": {"type": "array", "items": {"type": "string"}},
|
||||
"caption_strategies": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
},
|
||||
"video_posts": {
|
||||
"type": "object",
|
||||
"description": "Video post optimization for Facebook"
|
||||
"description": "Video post optimization for Facebook",
|
||||
"properties": {
|
||||
"video_length_guidelines": {"type": "array", "items": {"type": "string"}},
|
||||
"engagement_hooks": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
},
|
||||
"carousel_posts": {
|
||||
"type": "object",
|
||||
"description": "Carousel post optimization for Facebook"
|
||||
"description": "Carousel post optimization for Facebook",
|
||||
"properties": {
|
||||
"slide_structure": {"type": "array", "items": {"type": "string"}},
|
||||
"storytelling_flow": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
"""
|
||||
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
|
||||
@@ -514,7 +520,7 @@ Generate a platform-optimized persona adaptation that maintains brand consistenc
|
||||
|
||||
return min(score, 100.0)
|
||||
|
||||
def get_user_personas(self, user_id: int) -> List[Dict[str, Any]]:
|
||||
def get_user_personas(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get all personas for a user."""
|
||||
try:
|
||||
session = get_db_session()
|
||||
@@ -544,7 +550,7 @@ Generate a platform-optimized persona adaptation that maintains brand consistenc
|
||||
logger.error(f"Error getting user personas: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_persona_for_platform(self, user_id: int, platform: str) -> Optional[Dict[str, Any]]:
|
||||
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()
|
||||
|
||||
252
backend/services/persona_data_service.py
Normal file
252
backend/services/persona_data_service.py
Normal file
@@ -0,0 +1,252 @@
|
||||
"""
|
||||
Persona Data Service
|
||||
Direct service for working with PersonaData table from onboarding.
|
||||
Leverages the rich JSON structure for better content generation.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional, List
|
||||
from datetime import datetime
|
||||
from loguru import logger
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from services.database import get_db_session
|
||||
from models.onboarding import PersonaData, OnboardingSession
|
||||
|
||||
|
||||
class PersonaDataService:
|
||||
"""Service for working directly with PersonaData table."""
|
||||
|
||||
def __init__(self, db_session: Optional[Session] = None):
|
||||
self.db = db_session or get_db_session()
|
||||
|
||||
def get_user_persona_data(self, user_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get complete persona data for a user from PersonaData table."""
|
||||
try:
|
||||
# Get onboarding session for user
|
||||
session = self.db.query(OnboardingSession).filter(
|
||||
OnboardingSession.user_id == user_id
|
||||
).first()
|
||||
|
||||
if not session:
|
||||
logger.warning(f"No onboarding session found for user {user_id}")
|
||||
return None
|
||||
|
||||
# Get persona data
|
||||
persona_data = self.db.query(PersonaData).filter(
|
||||
PersonaData.session_id == session.id
|
||||
).first()
|
||||
|
||||
if not persona_data:
|
||||
logger.warning(f"No persona data found for user {user_id}")
|
||||
return None
|
||||
|
||||
return persona_data.to_dict()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting persona data for user {user_id}: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_platform_persona(self, user_id: str, platform: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get platform-specific persona data for a user."""
|
||||
try:
|
||||
persona_data = self.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
return None
|
||||
|
||||
platform_personas = persona_data.get('platform_personas', {})
|
||||
platform_data = platform_personas.get(platform)
|
||||
|
||||
if not platform_data:
|
||||
logger.warning(f"No {platform} persona found for user {user_id}")
|
||||
return None
|
||||
|
||||
# Return rich platform-specific data
|
||||
return {
|
||||
"platform": platform,
|
||||
"platform_persona": platform_data,
|
||||
"core_persona": persona_data.get('core_persona', {}),
|
||||
"quality_metrics": persona_data.get('quality_metrics', {}),
|
||||
"selected_platforms": persona_data.get('selected_platforms', []),
|
||||
"created_at": persona_data.get('created_at'),
|
||||
"updated_at": persona_data.get('updated_at')
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting {platform} persona for user {user_id}: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_all_platform_personas(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get all platform personas for a user."""
|
||||
try:
|
||||
persona_data = self.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
return {}
|
||||
|
||||
platform_personas = persona_data.get('platform_personas', {})
|
||||
|
||||
# Return structured data for all platforms
|
||||
result = {}
|
||||
for platform, platform_data in platform_personas.items():
|
||||
if isinstance(platform_data, dict) and 'error' not in platform_data:
|
||||
result[platform] = {
|
||||
"platform": platform,
|
||||
"platform_persona": platform_data,
|
||||
"core_persona": persona_data.get('core_persona', {}),
|
||||
"quality_metrics": persona_data.get('quality_metrics', {}),
|
||||
"created_at": persona_data.get('created_at'),
|
||||
"updated_at": persona_data.get('updated_at')
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting all platform personas for user {user_id}: {str(e)}")
|
||||
return {}
|
||||
|
||||
def get_core_persona(self, user_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get core persona data for a user."""
|
||||
try:
|
||||
persona_data = self.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
return None
|
||||
|
||||
return {
|
||||
"core_persona": persona_data.get('core_persona', {}),
|
||||
"quality_metrics": persona_data.get('quality_metrics', {}),
|
||||
"selected_platforms": persona_data.get('selected_platforms', []),
|
||||
"created_at": persona_data.get('created_at'),
|
||||
"updated_at": persona_data.get('updated_at')
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting core persona for user {user_id}: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_persona_quality_metrics(self, user_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get quality metrics for a user's persona."""
|
||||
try:
|
||||
persona_data = self.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
return None
|
||||
|
||||
return persona_data.get('quality_metrics', {})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting quality metrics for user {user_id}: {str(e)}")
|
||||
return None
|
||||
|
||||
def update_platform_persona(self, user_id: str, platform: str, updates: Dict[str, Any]) -> bool:
|
||||
"""Update platform-specific persona data."""
|
||||
try:
|
||||
# Get onboarding session for user
|
||||
session = self.db.query(OnboardingSession).filter(
|
||||
OnboardingSession.user_id == user_id
|
||||
).first()
|
||||
|
||||
if not session:
|
||||
logger.error(f"No onboarding session found for user {user_id}")
|
||||
return False
|
||||
|
||||
# Get persona data
|
||||
persona_data = self.db.query(PersonaData).filter(
|
||||
PersonaData.session_id == session.id
|
||||
).first()
|
||||
|
||||
if not persona_data:
|
||||
logger.error(f"No persona data found for user {user_id}")
|
||||
return False
|
||||
|
||||
# Update platform-specific data
|
||||
platform_personas = persona_data.platform_personas or {}
|
||||
if platform in platform_personas:
|
||||
platform_personas[platform].update(updates)
|
||||
persona_data.platform_personas = platform_personas
|
||||
persona_data.updated_at = datetime.utcnow()
|
||||
|
||||
self.db.commit()
|
||||
logger.info(f"Updated {platform} persona for user {user_id}")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"Platform {platform} not found for user {user_id}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating {platform} persona for user {user_id}: {str(e)}")
|
||||
self.db.rollback()
|
||||
return False
|
||||
|
||||
def save_platform_persona(self, user_id: str, platform: str, platform_data: Dict[str, Any]) -> bool:
|
||||
"""Save or create platform-specific persona data (creates if doesn't exist)."""
|
||||
try:
|
||||
# Get onboarding session
|
||||
session = self.db.query(OnboardingSession).filter(
|
||||
OnboardingSession.user_id == user_id
|
||||
).first()
|
||||
|
||||
if not session:
|
||||
logger.error(f"No onboarding session found for user {user_id}")
|
||||
return False
|
||||
|
||||
# Get persona data
|
||||
persona_data = self.db.query(PersonaData).filter(
|
||||
PersonaData.session_id == session.id
|
||||
).first()
|
||||
|
||||
if not persona_data:
|
||||
logger.error(f"No persona data found for user {user_id}")
|
||||
return False
|
||||
|
||||
# Update or create platform persona
|
||||
platform_personas = persona_data.platform_personas or {}
|
||||
platform_personas[platform] = platform_data # Create or overwrite
|
||||
persona_data.platform_personas = platform_personas
|
||||
persona_data.updated_at = datetime.utcnow()
|
||||
|
||||
self.db.commit()
|
||||
logger.info(f"Saved {platform} persona for user {user_id}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving {platform} persona for user {user_id}: {str(e)}")
|
||||
self.db.rollback()
|
||||
return False
|
||||
|
||||
def get_supported_platforms(self, user_id: str) -> List[str]:
|
||||
"""Get list of platforms for which personas exist."""
|
||||
try:
|
||||
persona_data = self.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
return []
|
||||
|
||||
platform_personas = persona_data.get('platform_personas', {})
|
||||
return [platform for platform, data in platform_personas.items()
|
||||
if isinstance(data, dict) and 'error' not in data]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting supported platforms for user {user_id}: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_persona_summary(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get a summary of persona data for a user."""
|
||||
try:
|
||||
persona_data = self.get_user_persona_data(user_id)
|
||||
if not persona_data:
|
||||
return {"error": "No persona data found"}
|
||||
|
||||
platform_personas = persona_data.get('platform_personas', {})
|
||||
quality_metrics = persona_data.get('quality_metrics', {})
|
||||
|
||||
return {
|
||||
"user_id": user_id,
|
||||
"has_core_persona": bool(persona_data.get('core_persona')),
|
||||
"platforms": list(platform_personas.keys()),
|
||||
"platform_count": len(platform_personas),
|
||||
"quality_score": quality_metrics.get('overall_score', 0),
|
||||
"selected_platforms": persona_data.get('selected_platforms', []),
|
||||
"created_at": persona_data.get('created_at'),
|
||||
"updated_at": persona_data.get('updated_at')
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting persona summary for user {user_id}: {str(e)}")
|
||||
return {"error": str(e)}
|
||||
Reference in New Issue
Block a user