""" Writing Persona Database Models Defines database schema for storing writing personas based on onboarding data analysis. Each persona represents a platform-specific writing style derived from user's onboarding data. """ from sqlalchemy import Column, Integer, String, Text, DateTime, Float, JSON, ForeignKey, Boolean from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from datetime import datetime Base = declarative_base() class WritingPersona(Base): """Main writing persona model that stores the core persona profile.""" __tablename__ = "writing_personas" # Primary fields id = Column(Integer, primary_key=True) 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 archetype = Column(String(100), nullable=True) # e.g., "The Pragmatic Futurist", "The Thoughtful Educator" core_belief = Column(Text, nullable=True) # Central philosophy or belief system brand_voice_description = Column(Text, nullable=True) # Detailed brand voice description # Linguistic Fingerprint - Quantitative Analysis linguistic_fingerprint = Column(JSON, nullable=True) # Complete linguistic analysis # Platform-specific adaptations platform_adaptations = Column(JSON, nullable=True) # How persona adapts across platforms # Source data tracking onboarding_session_id = Column(Integer, nullable=True) # Link to onboarding session source_website_analysis = Column(JSON, nullable=True) # Website analysis data used source_research_preferences = Column(JSON, nullable=True) # Research preferences used # AI Analysis metadata ai_analysis_version = Column(String(50), nullable=True) # Version of AI analysis used confidence_score = Column(Float, nullable=True) # AI confidence in persona accuracy analysis_date = Column(DateTime, default=datetime.utcnow) # Metadata created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) is_active = Column(Boolean, default=True) # Relationships platform_personas = relationship("PlatformPersona", back_populates="writing_persona", cascade="all, delete-orphan") def __repr__(self): return f"" def to_dict(self): """Convert model to dictionary.""" return { 'id': self.id, 'user_id': self.user_id, 'persona_name': self.persona_name, 'archetype': self.archetype, 'core_belief': self.core_belief, 'brand_voice_description': self.brand_voice_description, 'linguistic_fingerprint': self.linguistic_fingerprint, 'platform_adaptations': self.platform_adaptations, 'onboarding_session_id': self.onboarding_session_id, 'source_website_analysis': self.source_website_analysis, 'source_research_preferences': self.source_research_preferences, 'ai_analysis_version': self.ai_analysis_version, 'confidence_score': self.confidence_score, 'analysis_date': self.analysis_date.isoformat() if self.analysis_date else None, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, 'is_active': self.is_active } class PlatformPersona(Base): """Platform-specific persona adaptations for different social media platforms and blogging.""" __tablename__ = "platform_personas" # Primary fields id = Column(Integer, primary_key=True) writing_persona_id = Column(Integer, ForeignKey("writing_personas.id"), nullable=False) platform_type = Column(String(50), nullable=False) # twitter, linkedin, instagram, facebook, blog, medium, substack # Platform-specific linguistic constraints sentence_metrics = Column(JSON, nullable=True) # Platform-optimized sentence structure lexical_features = Column(JSON, nullable=True) # Platform-specific vocabulary and phrases rhetorical_devices = Column(JSON, nullable=True) # Platform-appropriate rhetorical patterns tonal_range = Column(JSON, nullable=True) # Permitted tones for this platform stylistic_constraints = Column(JSON, nullable=True) # Platform formatting rules # Platform-specific content guidelines content_format_rules = Column(JSON, nullable=True) # Character limits, hashtag usage, etc. engagement_patterns = Column(JSON, nullable=True) # How to engage on this platform posting_frequency = Column(JSON, nullable=True) # Optimal posting schedule content_types = Column(JSON, nullable=True) # Preferred content types for platform # Performance optimization platform_best_practices = Column(JSON, nullable=True) # Platform-specific best practices algorithm_considerations = Column(JSON, nullable=True) # Platform algorithm optimization # Metadata created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) is_active = Column(Boolean, default=True) # Relationships writing_persona = relationship("WritingPersona", back_populates="platform_personas") def __repr__(self): return f"" def to_dict(self): """Convert model to dictionary.""" result = { 'id': self.id, 'writing_persona_id': self.writing_persona_id, 'platform_type': self.platform_type, 'sentence_metrics': self.sentence_metrics, 'lexical_features': self.lexical_features, 'rhetorical_devices': self.rhetorical_devices, 'tonal_range': self.tonal_range, 'stylistic_constraints': self.stylistic_constraints, 'content_format_rules': self.content_format_rules, 'engagement_patterns': self.engagement_patterns, 'posting_frequency': self.posting_frequency, 'content_types': self.content_types, 'platform_best_practices': self.platform_best_practices, 'algorithm_considerations': self.algorithm_considerations, 'created_at': self.created_at.isoformat() if self.created_at else None, 'updated_at': self.updated_at.isoformat() if self.updated_at else None, 'is_active': self.is_active } # Add LinkedIn-specific fields if this is a LinkedIn persona if self.platform_type.lower() == "linkedin" and self.algorithm_considerations: linkedin_data = self.algorithm_considerations if isinstance(linkedin_data, dict): result.update({ 'professional_networking': linkedin_data.get('professional_networking', {}), 'linkedin_features': linkedin_data.get('linkedin_features', {}), 'algorithm_optimization': linkedin_data.get('algorithm_optimization', {}), 'professional_context_optimization': linkedin_data.get('professional_context_optimization', {}) }) return result class PersonaAnalysisResult(Base): """Stores AI analysis results used to generate personas.""" __tablename__ = "persona_analysis_results" id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False) writing_persona_id = Column(Integer, ForeignKey("writing_personas.id"), nullable=True) # Analysis input data analysis_prompt = Column(Text, nullable=True) # The prompt used for analysis input_data = Column(JSON, nullable=True) # Raw input data from onboarding # AI Analysis results linguistic_analysis = Column(JSON, nullable=True) # Detailed linguistic fingerprint analysis personality_analysis = Column(JSON, nullable=True) # Personality and archetype analysis platform_recommendations = Column(JSON, nullable=True) # Platform-specific recommendations style_guidelines = Column(JSON, nullable=True) # Generated style guidelines # Quality metrics analysis_confidence = Column(Float, nullable=True) # AI confidence in analysis data_sufficiency_score = Column(Float, nullable=True) # How much data was available for analysis recommendation_quality = Column(Float, nullable=True) # Quality of generated recommendations # AI service metadata ai_provider = Column(String(50), nullable=True) # gemini, openai, anthropic model_version = Column(String(100), nullable=True) # Specific model version used processing_time = Column(Float, nullable=True) # Processing time in seconds # Metadata created_at = Column(DateTime, default=datetime.utcnow) def __repr__(self): return f"" def to_dict(self): """Convert model to dictionary.""" return { 'id': self.id, 'user_id': self.user_id, 'writing_persona_id': self.writing_persona_id, 'analysis_prompt': self.analysis_prompt, 'input_data': self.input_data, 'linguistic_analysis': self.linguistic_analysis, 'personality_analysis': self.personality_analysis, 'platform_recommendations': self.platform_recommendations, 'style_guidelines': self.style_guidelines, 'analysis_confidence': self.analysis_confidence, 'data_sufficiency_score': self.data_sufficiency_score, 'recommendation_quality': self.recommendation_quality, 'ai_provider': self.ai_provider, 'model_version': self.model_version, 'processing_time': self.processing_time, 'created_at': self.created_at.isoformat() if self.created_at else None } class PersonaValidationResult(Base): """Stores validation results for generated personas.""" __tablename__ = "persona_validation_results" id = Column(Integer, primary_key=True) writing_persona_id = Column(Integer, ForeignKey("writing_personas.id"), nullable=False) platform_persona_id = Column(Integer, ForeignKey("platform_personas.id"), nullable=True) # Validation metrics stylometric_accuracy = Column(Float, nullable=True) # How well persona matches original style consistency_score = Column(Float, nullable=True) # Consistency across generated content platform_compliance = Column(Float, nullable=True) # How well adapted to platform constraints # Test results sample_outputs = Column(JSON, nullable=True) # Sample content generated with persona validation_feedback = Column(JSON, nullable=True) # User or automated feedback improvement_suggestions = Column(JSON, nullable=True) # Suggestions for persona refinement # Metadata validation_date = Column(DateTime, default=datetime.utcnow) validator_type = Column(String(50), nullable=True) # automated, user, ai_review def __repr__(self): return f"" def to_dict(self): """Convert model to dictionary.""" return { 'id': self.id, 'writing_persona_id': self.writing_persona_id, 'platform_persona_id': self.platform_persona_id, 'stylometric_accuracy': self.stylometric_accuracy, 'consistency_score': self.consistency_score, 'platform_compliance': self.platform_compliance, 'sample_outputs': self.sample_outputs, 'validation_feedback': self.validation_feedback, 'improvement_suggestions': self.improvement_suggestions, 'validation_date': self.validation_date.isoformat() if self.validation_date else None, 'validator_type': self.validator_type }