ALwrity Version 0.5.0 (Fastapi + React )

This commit is contained in:
ajaysi
2025-08-06 12:48:02 +05:30
parent f28a919caa
commit 32f97fa6b3
476 changed files with 115544 additions and 28747 deletions

View File

@@ -0,0 +1 @@
# Models package for Alwrity

View File

@@ -0,0 +1,260 @@
"""Pydantic models for component logic requests and responses."""
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, EmailStr, validator
import re
# AI Research Models
class UserInfoRequest(BaseModel):
"""Request model for user information validation."""
full_name: str
email: str
company: str
role: str
@validator('full_name')
def validate_full_name(cls, v):
if not v or len(v.strip()) < 2:
raise ValueError('Full name must be at least 2 characters long')
return v.strip()
@validator('email')
def validate_email(cls, v):
# Basic email validation
email_pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
if not email_pattern.match(v):
raise ValueError('Invalid email format')
return v.lower()
@validator('company')
def validate_company(cls, v):
if not v or len(v.strip()) < 1:
raise ValueError('Company name is required')
return v.strip()
@validator('role')
def validate_role(cls, v):
valid_roles = ["Content Creator", "Marketing Manager", "Business Owner", "Other"]
if v not in valid_roles:
raise ValueError(f'Role must be one of: {", ".join(valid_roles)}')
return v
class ResearchPreferencesRequest(BaseModel):
"""Request model for research preferences configuration."""
research_depth: str
content_types: List[str]
auto_research: bool
factual_content: bool = True # Default to True
@validator('research_depth')
def validate_research_depth(cls, v):
valid_depths = ["Basic", "Standard", "Deep", "Comprehensive"]
if v not in valid_depths:
raise ValueError(f'Research depth must be one of: {", ".join(valid_depths)}')
return v
@validator('content_types')
def validate_content_types(cls, v):
valid_types = ["Blog Posts", "Social Media", "Technical Articles", "News", "Academic Papers"]
if not v:
raise ValueError('At least one content type must be selected')
for content_type in v:
if content_type not in valid_types:
raise ValueError(f'Invalid content type: {content_type}')
return v
class ResearchRequest(BaseModel):
"""Request model for research processing."""
topic: str
preferences: ResearchPreferencesRequest
@validator('topic')
def validate_topic(cls, v):
if not v or len(v.strip()) < 3:
raise ValueError('Topic must be at least 3 characters long')
return v.strip()
class UserInfoResponse(BaseModel):
"""Response model for user information validation."""
valid: bool
user_info: Optional[Dict[str, Any]] = None
errors: List[str] = []
class ResearchPreferencesResponse(BaseModel):
"""Response model for research preferences configuration."""
valid: bool
preferences: Optional[Dict[str, Any]] = None
errors: List[str] = []
class ResearchResponse(BaseModel):
"""Response model for research processing."""
success: bool
topic: str
results: Optional[Dict[str, Any]] = None
error: Optional[str] = None
# Personalization Models
class ContentStyleRequest(BaseModel):
"""Request model for content style configuration."""
writing_style: str
tone: str
content_length: str
@validator('writing_style')
def validate_writing_style(cls, v):
valid_styles = ["Professional", "Casual", "Technical", "Conversational", "Academic"]
if v not in valid_styles:
raise ValueError(f'Writing style must be one of: {", ".join(valid_styles)}')
return v
@validator('tone')
def validate_tone(cls, v):
valid_tones = ["Formal", "Semi-Formal", "Neutral", "Friendly", "Humorous"]
if v not in valid_tones:
raise ValueError(f'Tone must be one of: {", ".join(valid_tones)}')
return v
@validator('content_length')
def validate_content_length(cls, v):
valid_lengths = ["Concise", "Standard", "Detailed", "Comprehensive"]
if v not in valid_lengths:
raise ValueError(f'Content length must be one of: {", ".join(valid_lengths)}')
return v
class BrandVoiceRequest(BaseModel):
"""Request model for brand voice configuration."""
personality_traits: List[str]
voice_description: Optional[str] = None
keywords: Optional[str] = None
@validator('personality_traits')
def validate_personality_traits(cls, v):
valid_traits = ["Professional", "Innovative", "Friendly", "Trustworthy", "Creative", "Expert"]
if not v:
raise ValueError('At least one personality trait must be selected')
for trait in v:
if trait not in valid_traits:
raise ValueError(f'Invalid personality trait: {trait}')
return v
@validator('voice_description')
def validate_voice_description(cls, v):
if v and len(v.strip()) < 10:
raise ValueError('Voice description must be at least 10 characters long')
return v.strip() if v else None
class AdvancedSettingsRequest(BaseModel):
"""Request model for advanced content generation settings."""
seo_optimization: bool
readability_level: str
content_structure: List[str]
@validator('readability_level')
def validate_readability_level(cls, v):
valid_levels = ["Simple", "Standard", "Advanced", "Expert"]
if v not in valid_levels:
raise ValueError(f'Readability level must be one of: {", ".join(valid_levels)}')
return v
@validator('content_structure')
def validate_content_structure(cls, v):
valid_structures = ["Introduction", "Key Points", "Examples", "Conclusion", "Call-to-Action"]
if not v:
raise ValueError('At least one content structure element must be selected')
for structure in v:
if structure not in valid_structures:
raise ValueError(f'Invalid content structure: {structure}')
return v
class PersonalizationSettingsRequest(BaseModel):
"""Request model for complete personalization settings."""
content_style: ContentStyleRequest
brand_voice: BrandVoiceRequest
advanced_settings: AdvancedSettingsRequest
class ContentStyleResponse(BaseModel):
"""Response model for content style validation."""
valid: bool
style_config: Optional[Dict[str, Any]] = None
errors: List[str] = []
class BrandVoiceResponse(BaseModel):
"""Response model for brand voice configuration."""
valid: bool
brand_config: Optional[Dict[str, Any]] = None
errors: List[str] = []
class PersonalizationSettingsResponse(BaseModel):
"""Response model for complete personalization settings."""
valid: bool
settings: Optional[Dict[str, Any]] = None
errors: List[str] = []
# Research Utilities Models
class ResearchTopicRequest(BaseModel):
"""Request model for topic research."""
topic: str
api_keys: Dict[str, str]
@validator('topic')
def validate_topic(cls, v):
if not v or len(v.strip()) < 3:
raise ValueError('Topic must be at least 3 characters long')
return v.strip()
class ResearchResultResponse(BaseModel):
"""Response model for research results."""
success: bool
topic: str
data: Optional[Dict[str, Any]] = None
error: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None
# Style Detection Models
class StyleAnalysisRequest(BaseModel):
"""Request model for style analysis."""
content: Dict[str, Any]
analysis_type: str = "comprehensive" # comprehensive, patterns, guidelines
class StyleAnalysisResponse(BaseModel):
"""Response model for style analysis."""
success: bool
analysis: Optional[Dict[str, Any]] = None
patterns: Optional[Dict[str, Any]] = None
guidelines: Optional[Dict[str, Any]] = None
error: Optional[str] = None
timestamp: str
class WebCrawlRequest(BaseModel):
"""Request model for web crawling."""
url: Optional[str] = None
text_sample: Optional[str] = None
class WebCrawlResponse(BaseModel):
"""Response model for web crawling."""
success: bool
content: Optional[Dict[str, Any]] = None
metrics: Optional[Dict[str, Any]] = None
error: Optional[str] = None
timestamp: str
class StyleDetectionRequest(BaseModel):
"""Request model for complete style detection workflow."""
url: Optional[str] = None
text_sample: Optional[str] = None
include_patterns: bool = True
include_guidelines: bool = True
class StyleDetectionResponse(BaseModel):
"""Response model for complete style detection workflow."""
success: bool
crawl_result: Optional[Dict[str, Any]] = None
style_analysis: Optional[Dict[str, Any]] = None
style_patterns: Optional[Dict[str, Any]] = None
style_guidelines: Optional[Dict[str, Any]] = None
error: Optional[str] = None
warning: Optional[str] = None
timestamp: str

View File

@@ -0,0 +1,239 @@
"""
Content Planning Database Models
Defines the database schema for content strategy, calendar events, and analytics.
"""
from sqlalchemy import Column, Integer, String, Text, DateTime, Float, JSON, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
Base = declarative_base()
class ContentStrategy(Base):
"""Content Strategy model."""
__tablename__ = "content_strategies"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
name = Column(String(255), nullable=False)
industry = Column(String(100), nullable=True)
target_audience = Column(JSON, nullable=True) # Store audience demographics and preferences
content_pillars = Column(JSON, nullable=True) # Store content pillar definitions
ai_recommendations = Column(JSON, nullable=True) # Store AI-generated recommendations
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
calendar_events = relationship("CalendarEvent", back_populates="strategy")
analytics = relationship("ContentAnalytics", back_populates="strategy")
def __repr__(self):
return f"<ContentStrategy(id={self.id}, name='{self.name}', industry='{self.industry}')>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'user_id': self.user_id,
'name': self.name,
'industry': self.industry,
'target_audience': self.target_audience,
'content_pillars': self.content_pillars,
'ai_recommendations': self.ai_recommendations,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class CalendarEvent(Base):
"""Calendar Event model."""
__tablename__ = "calendar_events"
id = Column(Integer, primary_key=True)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=False)
title = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
content_type = Column(String(50), nullable=False) # blog_post, video, social_post, etc.
platform = Column(String(50), nullable=False) # website, linkedin, youtube, etc.
scheduled_date = Column(DateTime, nullable=False)
status = Column(String(20), default="draft") # draft, scheduled, published, cancelled
ai_recommendations = Column(JSON, nullable=True) # Store AI recommendations for the event
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
strategy = relationship("ContentStrategy", back_populates="calendar_events")
analytics = relationship("ContentAnalytics", back_populates="event")
def __repr__(self):
return f"<CalendarEvent(id={self.id}, title='{self.title}', status='{self.status}')>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'strategy_id': self.strategy_id,
'title': self.title,
'description': self.description,
'content_type': self.content_type,
'platform': self.platform,
'scheduled_date': self.scheduled_date.isoformat() if self.scheduled_date else None,
'status': self.status,
'ai_recommendations': self.ai_recommendations,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class ContentAnalytics(Base):
"""Content Analytics model."""
__tablename__ = "content_analytics"
id = Column(Integer, primary_key=True)
event_id = Column(Integer, ForeignKey("calendar_events.id"), nullable=True)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
platform = Column(String(50), nullable=False) # website, linkedin, youtube, etc.
metrics = Column(JSON, nullable=True) # Store various performance metrics
performance_score = Column(Float, nullable=True) # Overall performance score
recorded_at = Column(DateTime, default=datetime.utcnow)
# Relationships
event = relationship("CalendarEvent", back_populates="analytics")
strategy = relationship("ContentStrategy", back_populates="analytics")
def __repr__(self):
return f"<ContentAnalytics(id={self.id}, platform='{self.platform}', score={self.performance_score})>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'event_id': self.event_id,
'strategy_id': self.strategy_id,
'platform': self.platform,
'metrics': self.metrics,
'performance_score': self.performance_score,
'recorded_at': self.recorded_at.isoformat() if self.recorded_at else None
}
class ContentGapAnalysis(Base):
"""Content Gap Analysis model."""
__tablename__ = "content_gap_analyses"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
website_url = Column(String(500), nullable=False)
competitor_urls = Column(JSON, nullable=True) # Store competitor URLs
target_keywords = Column(JSON, nullable=True) # Store target keywords
analysis_results = Column(JSON, nullable=True) # Store complete analysis results
recommendations = Column(JSON, nullable=True) # Store AI recommendations
opportunities = Column(JSON, nullable=True) # Store identified opportunities
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<ContentGapAnalysis(id={self.id}, website='{self.website_url}')>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'user_id': self.user_id,
'website_url': self.website_url,
'competitor_urls': self.competitor_urls,
'target_keywords': self.target_keywords,
'analysis_results': self.analysis_results,
'recommendations': self.recommendations,
'opportunities': self.opportunities,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class ContentRecommendation(Base):
"""Content Recommendation model."""
__tablename__ = "content_recommendations"
id = Column(Integer, primary_key=True)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
user_id = Column(Integer, nullable=False)
recommendation_type = Column(String(50), nullable=False) # blog_post, video, case_study, etc.
title = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
target_keywords = Column(JSON, nullable=True) # Store target keywords
estimated_length = Column(String(100), nullable=True) # Estimated content length
priority = Column(String(20), default="medium") # low, medium, high
platforms = Column(JSON, nullable=True) # Store target platforms
estimated_performance = Column(String(100), nullable=True) # Performance prediction
status = Column(String(20), default="pending") # pending, accepted, rejected, implemented
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
strategy = relationship("ContentStrategy")
def __repr__(self):
return f"<ContentRecommendation(id={self.id}, title='{self.title}', type='{self.recommendation_type}')>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'strategy_id': self.strategy_id,
'user_id': self.user_id,
'recommendation_type': self.recommendation_type,
'title': self.title,
'description': self.description,
'target_keywords': self.target_keywords,
'estimated_length': self.estimated_length,
'priority': self.priority,
'platforms': self.platforms,
'estimated_performance': self.estimated_performance,
'status': self.status,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class AIAnalysisResult(Base):
"""AI Analysis Result model for storing AI-generated insights and recommendations."""
__tablename__ = "ai_analysis_results"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
analysis_type = Column(String(50), nullable=False) # performance_trends, strategic_intelligence, content_evolution, gap_analysis
insights = Column(JSON, nullable=True) # Store AI-generated insights
recommendations = Column(JSON, nullable=True) # Store AI-generated recommendations
performance_metrics = Column(JSON, nullable=True) # Store performance data
personalized_data_used = Column(JSON, nullable=True) # Store the onboarding data used for personalization
processing_time = Column(Float, nullable=True) # Store processing time in seconds
ai_service_status = Column(String(20), default="operational") # operational, fallback, error
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
strategy = relationship("ContentStrategy")
def __repr__(self):
return f"<AIAnalysisResult(id={self.id}, type='{self.analysis_type}', user_id={self.user_id})>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'user_id': self.user_id,
'strategy_id': self.strategy_id,
'analysis_type': self.analysis_type,
'insights': self.insights,
'recommendations': self.recommendations,
'performance_metrics': self.performance_metrics,
'personalized_data_used': self.personalized_data_used,
'processing_time': self.processing_time,
'ai_service_status': self.ai_service_status,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}

View File

@@ -0,0 +1,269 @@
"""
Enhanced Calendar Models for AI-Powered Content Planning
Defines additional database schema for intelligent calendar generation and optimization.
"""
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 ContentCalendarTemplate(Base):
"""Template for industry-specific content calendars."""
__tablename__ = "content_calendar_templates"
id = Column(Integer, primary_key=True)
industry = Column(String(100), nullable=False)
business_size = Column(String(50), nullable=True) # startup, sme, enterprise
content_pillars = Column(JSON, nullable=True) # Core content themes
posting_frequency = Column(JSON, nullable=True) # Platform-specific frequency
platform_strategies = Column(JSON, nullable=True) # Platform-specific content types
optimal_timing = Column(JSON, nullable=True) # Best posting times per platform
content_mix = Column(JSON, nullable=True) # Content type distribution
seasonal_themes = Column(JSON, nullable=True) # Seasonal content opportunities
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<ContentCalendarTemplate(id={self.id}, industry='{self.industry}')>"
def to_dict(self):
return {
'id': self.id,
'industry': self.industry,
'business_size': self.business_size,
'content_pillars': self.content_pillars,
'posting_frequency': self.posting_frequency,
'platform_strategies': self.platform_strategies,
'optimal_timing': self.optimal_timing,
'content_mix': self.content_mix,
'seasonal_themes': self.seasonal_themes,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class AICalendarRecommendation(Base):
"""AI-generated calendar recommendations and suggestions."""
__tablename__ = "ai_calendar_recommendations"
id = Column(Integer, primary_key=True)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
user_id = Column(Integer, nullable=False)
recommendation_type = Column(String(50), nullable=False) # calendar_generation, content_optimization, performance_analysis
content_suggestions = Column(JSON, nullable=True) # Suggested content topics and themes
optimal_timing = Column(JSON, nullable=True) # Recommended posting times
performance_prediction = Column(JSON, nullable=True) # Predicted performance metrics
platform_recommendations = Column(JSON, nullable=True) # Platform-specific suggestions
content_repurposing = Column(JSON, nullable=True) # Repurposing opportunities
trending_topics = Column(JSON, nullable=True) # Trending topics to incorporate
competitor_insights = Column(JSON, nullable=True) # Competitor analysis insights
ai_confidence = Column(Float, nullable=True) # AI confidence score
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
strategy = relationship("ContentStrategy")
def __repr__(self):
return f"<AICalendarRecommendation(id={self.id}, type='{self.recommendation_type}')>"
def to_dict(self):
return {
'id': self.id,
'strategy_id': self.strategy_id,
'user_id': self.user_id,
'recommendation_type': self.recommendation_type,
'content_suggestions': self.content_suggestions,
'optimal_timing': self.optimal_timing,
'performance_prediction': self.performance_prediction,
'platform_recommendations': self.platform_recommendations,
'content_repurposing': self.content_repurposing,
'trending_topics': self.trending_topics,
'competitor_insights': self.competitor_insights,
'ai_confidence': self.ai_confidence,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class ContentPerformanceTracking(Base):
"""Detailed content performance tracking and analytics."""
__tablename__ = "content_performance_tracking"
id = Column(Integer, primary_key=True)
event_id = Column(Integer, ForeignKey("calendar_events.id"), nullable=True)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
platform = Column(String(50), nullable=False) # website, linkedin, instagram, etc.
content_type = Column(String(50), nullable=False) # blog_post, video, social_post, etc.
metrics = Column(JSON, nullable=True) # Engagement, reach, clicks, conversions, etc.
performance_score = Column(Float, nullable=True) # Overall performance score (0-100)
audience_demographics = Column(JSON, nullable=True) # Audience insights
engagement_rate = Column(Float, nullable=True) # Engagement rate percentage
reach_count = Column(Integer, nullable=True) # Total reach
click_count = Column(Integer, nullable=True) # Total clicks
conversion_count = Column(Integer, nullable=True) # Total conversions
roi = Column(Float, nullable=True) # Return on investment
recorded_at = Column(DateTime, default=datetime.utcnow)
# Relationships
event = relationship("CalendarEvent")
strategy = relationship("ContentStrategy")
def __repr__(self):
return f"<ContentPerformanceTracking(id={self.id}, platform='{self.platform}', score={self.performance_score})>"
def to_dict(self):
return {
'id': self.id,
'event_id': self.event_id,
'strategy_id': self.strategy_id,
'platform': self.platform,
'content_type': self.content_type,
'metrics': self.metrics,
'performance_score': self.performance_score,
'audience_demographics': self.audience_demographics,
'engagement_rate': self.engagement_rate,
'reach_count': self.reach_count,
'click_count': self.click_count,
'conversion_count': self.conversion_count,
'roi': self.roi,
'recorded_at': self.recorded_at.isoformat() if self.recorded_at else None
}
class ContentTrendAnalysis(Base):
"""Trend analysis and topic recommendations."""
__tablename__ = "content_trend_analysis"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
industry = Column(String(100), nullable=False)
trending_topics = Column(JSON, nullable=True) # Trending topics in the industry
keyword_opportunities = Column(JSON, nullable=True) # High-value keywords
content_gaps = Column(JSON, nullable=True) # Identified content gaps
seasonal_opportunities = Column(JSON, nullable=True) # Seasonal content opportunities
competitor_analysis = Column(JSON, nullable=True) # Competitor content analysis
viral_potential = Column(JSON, nullable=True) # Content with viral potential
audience_interests = Column(JSON, nullable=True) # Current audience interests
analysis_date = Column(DateTime, default=datetime.utcnow)
created_at = Column(DateTime, default=datetime.utcnow)
# Relationships
strategy = relationship("ContentStrategy")
def __repr__(self):
return f"<ContentTrendAnalysis(id={self.id}, industry='{self.industry}')>"
def to_dict(self):
return {
'id': self.id,
'user_id': self.user_id,
'strategy_id': self.strategy_id,
'industry': self.industry,
'trending_topics': self.trending_topics,
'keyword_opportunities': self.keyword_opportunities,
'content_gaps': self.content_gaps,
'seasonal_opportunities': self.seasonal_opportunities,
'competitor_analysis': self.competitor_analysis,
'viral_potential': self.viral_potential,
'audience_interests': self.audience_interests,
'analysis_date': self.analysis_date.isoformat() if self.analysis_date else None,
'created_at': self.created_at.isoformat() if self.created_at else None
}
class ContentOptimization(Base):
"""Content optimization recommendations and suggestions."""
__tablename__ = "content_optimizations"
id = Column(Integer, primary_key=True)
event_id = Column(Integer, ForeignKey("calendar_events.id"), nullable=True)
user_id = Column(Integer, nullable=False)
original_content = Column(JSON, nullable=True) # Original content details
optimized_content = Column(JSON, nullable=True) # Optimized content suggestions
platform_adaptations = Column(JSON, nullable=True) # Platform-specific adaptations
visual_recommendations = Column(JSON, nullable=True) # Visual content suggestions
hashtag_suggestions = Column(JSON, nullable=True) # Hashtag recommendations
keyword_optimization = Column(JSON, nullable=True) # SEO keyword optimization
tone_adjustments = Column(JSON, nullable=True) # Tone and style adjustments
length_optimization = Column(JSON, nullable=True) # Content length optimization
performance_prediction = Column(JSON, nullable=True) # Predicted performance
optimization_score = Column(Float, nullable=True) # Optimization effectiveness score
created_at = Column(DateTime, default=datetime.utcnow)
# Relationships
event = relationship("CalendarEvent")
def __repr__(self):
return f"<ContentOptimization(id={self.id}, score={self.optimization_score})>"
def to_dict(self):
return {
'id': self.id,
'event_id': self.event_id,
'user_id': self.user_id,
'original_content': self.original_content,
'optimized_content': self.optimized_content,
'platform_adaptations': self.platform_adaptations,
'visual_recommendations': self.visual_recommendations,
'hashtag_suggestions': self.hashtag_suggestions,
'keyword_optimization': self.keyword_optimization,
'tone_adjustments': self.tone_adjustments,
'length_optimization': self.length_optimization,
'performance_prediction': self.performance_prediction,
'optimization_score': self.optimization_score,
'created_at': self.created_at.isoformat() if self.created_at else None
}
class CalendarGenerationSession(Base):
"""AI calendar generation sessions and results."""
__tablename__ = "calendar_generation_sessions"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
strategy_id = Column(Integer, ForeignKey("content_strategies.id"), nullable=True)
session_type = Column(String(50), nullable=False) # monthly, weekly, custom
generation_params = Column(JSON, nullable=True) # Parameters used for generation
generated_calendar = Column(JSON, nullable=True) # Generated calendar data
ai_insights = Column(JSON, nullable=True) # AI insights and recommendations
performance_predictions = Column(JSON, nullable=True) # Performance predictions
content_themes = Column(JSON, nullable=True) # Content themes and pillars
platform_distribution = Column(JSON, nullable=True) # Platform content distribution
optimal_schedule = Column(JSON, nullable=True) # Optimal posting schedule
repurposing_opportunities = Column(JSON, nullable=True) # Content repurposing
generation_status = Column(String(20), default="processing") # processing, completed, failed
ai_confidence = Column(Float, nullable=True) # Overall AI confidence
processing_time = Column(Float, nullable=True) # Processing time in seconds
created_at = Column(DateTime, default=datetime.utcnow)
# Relationships
strategy = relationship("ContentStrategy")
def __repr__(self):
return f"<CalendarGenerationSession(id={self.id}, type='{self.session_type}', status='{self.generation_status}')>"
def to_dict(self):
return {
'id': self.id,
'user_id': self.user_id,
'strategy_id': self.strategy_id,
'session_type': self.session_type,
'generation_params': self.generation_params,
'generated_calendar': self.generated_calendar,
'ai_insights': self.ai_insights,
'performance_predictions': self.performance_predictions,
'content_themes': self.content_themes,
'platform_distribution': self.platform_distribution,
'optimal_schedule': self.optimal_schedule,
'repurposing_opportunities': self.repurposing_opportunities,
'generation_status': self.generation_status,
'ai_confidence': self.ai_confidence,
'processing_time': self.processing_time,
'created_at': self.created_at.isoformat() if self.created_at else None
}

View File

@@ -0,0 +1,280 @@
"""
Enhanced Strategy Database Models
Defines the enhanced database schema for content strategy with 30+ strategic inputs.
"""
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 EnhancedContentStrategy(Base):
"""Enhanced Content Strategy model with 30+ strategic inputs."""
__tablename__ = "enhanced_content_strategies"
# Primary fields
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
name = Column(String(255), nullable=False)
industry = Column(String(100), nullable=True)
# Business Context (8 inputs)
business_objectives = Column(JSON, nullable=True) # Primary and secondary business goals
target_metrics = Column(JSON, nullable=True) # KPIs and success metrics
content_budget = Column(Float, nullable=True) # Monthly/annual content budget
team_size = Column(Integer, nullable=True) # Content team size
implementation_timeline = Column(String(100), nullable=True) # 3 months, 6 months, 1 year, etc.
market_share = Column(String(50), nullable=True) # Current market share percentage
competitive_position = Column(String(50), nullable=True) # Leader, challenger, niche, emerging
performance_metrics = Column(JSON, nullable=True) # Current performance data
# Audience Intelligence (6 inputs)
content_preferences = Column(JSON, nullable=True) # Preferred content formats and topics
consumption_patterns = Column(JSON, nullable=True) # When and how audience consumes content
audience_pain_points = Column(JSON, nullable=True) # Key challenges and pain points
buying_journey = Column(JSON, nullable=True) # Customer journey stages and touchpoints
seasonal_trends = Column(JSON, nullable=True) # Seasonal content opportunities
engagement_metrics = Column(JSON, nullable=True) # Current engagement data
# Competitive Intelligence (5 inputs)
top_competitors = Column(JSON, nullable=True) # List of main competitors
competitor_content_strategies = Column(JSON, nullable=True) # Analysis of competitor approaches
market_gaps = Column(JSON, nullable=True) # Identified market opportunities
industry_trends = Column(JSON, nullable=True) # Current industry trends
emerging_trends = Column(JSON, nullable=True) # Upcoming trends and opportunities
# Content Strategy (7 inputs)
preferred_formats = Column(JSON, nullable=True) # Blog posts, videos, infographics, etc.
content_mix = Column(JSON, nullable=True) # Distribution of content types
content_frequency = Column(String(50), nullable=True) # Daily, weekly, monthly, etc.
optimal_timing = Column(JSON, nullable=True) # Best times for publishing
quality_metrics = Column(JSON, nullable=True) # Content quality standards
editorial_guidelines = Column(JSON, nullable=True) # Style and tone guidelines
brand_voice = Column(JSON, nullable=True) # Brand personality and voice
# Performance & Analytics (4 inputs)
traffic_sources = Column(JSON, nullable=True) # Primary traffic sources
conversion_rates = Column(JSON, nullable=True) # Current conversion data
content_roi_targets = Column(JSON, nullable=True) # ROI goals and targets
ab_testing_capabilities = Column(Boolean, default=False) # A/B testing availability
# Legacy fields for backward compatibility
target_audience = Column(JSON, nullable=True) # Store audience demographics and preferences
content_pillars = Column(JSON, nullable=True) # Store content pillar definitions
ai_recommendations = Column(JSON, nullable=True) # Store AI-generated recommendations
# Enhanced AI Analysis fields
comprehensive_ai_analysis = Column(JSON, nullable=True) # Enhanced AI analysis results
onboarding_data_used = Column(JSON, nullable=True) # Track onboarding data integration
strategic_scores = Column(JSON, nullable=True) # Strategic performance scores
market_positioning = Column(JSON, nullable=True) # Market positioning analysis
competitive_advantages = Column(JSON, nullable=True) # Identified competitive advantages
strategic_risks = Column(JSON, nullable=True) # Risk assessment
opportunity_analysis = Column(JSON, nullable=True) # Opportunity identification
# Metadata
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
completion_percentage = Column(Float, default=0.0) # Track input completion
data_source_transparency = Column(JSON, nullable=True) # Track data sources for auto-population
def __repr__(self):
return f"<EnhancedContentStrategy(id={self.id}, name='{self.name}', industry='{self.industry}')>"
def to_dict(self):
"""Convert model to dictionary with enhanced structure."""
return {
'id': self.id,
'user_id': self.user_id,
'name': self.name,
'industry': self.industry,
# Business Context
'business_objectives': self.business_objectives,
'target_metrics': self.target_metrics,
'content_budget': self.content_budget,
'team_size': self.team_size,
'implementation_timeline': self.implementation_timeline,
'market_share': self.market_share,
'competitive_position': self.competitive_position,
'performance_metrics': self.performance_metrics,
# Audience Intelligence
'content_preferences': self.content_preferences,
'consumption_patterns': self.consumption_patterns,
'audience_pain_points': self.audience_pain_points,
'buying_journey': self.buying_journey,
'seasonal_trends': self.seasonal_trends,
'engagement_metrics': self.engagement_metrics,
# Competitive Intelligence
'top_competitors': self.top_competitors,
'competitor_content_strategies': self.competitor_content_strategies,
'market_gaps': self.market_gaps,
'industry_trends': self.industry_trends,
'emerging_trends': self.emerging_trends,
# Content Strategy
'preferred_formats': self.preferred_formats,
'content_mix': self.content_mix,
'content_frequency': self.content_frequency,
'optimal_timing': self.optimal_timing,
'quality_metrics': self.quality_metrics,
'editorial_guidelines': self.editorial_guidelines,
'brand_voice': self.brand_voice,
# Performance & Analytics
'traffic_sources': self.traffic_sources,
'conversion_rates': self.conversion_rates,
'content_roi_targets': self.content_roi_targets,
'ab_testing_capabilities': self.ab_testing_capabilities,
# Legacy fields
'target_audience': self.target_audience,
'content_pillars': self.content_pillars,
'ai_recommendations': self.ai_recommendations,
# Enhanced AI Analysis
'comprehensive_ai_analysis': self.comprehensive_ai_analysis,
'onboarding_data_used': self.onboarding_data_used,
'strategic_scores': self.strategic_scores,
'market_positioning': self.market_positioning,
'competitive_advantages': self.competitive_advantages,
'strategic_risks': self.strategic_risks,
'opportunity_analysis': self.opportunity_analysis,
# Metadata
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
'completion_percentage': self.completion_percentage,
'data_source_transparency': self.data_source_transparency
}
def calculate_completion_percentage(self):
"""Calculate the percentage of required fields that have been filled."""
required_fields = [
'business_objectives', 'target_metrics', 'content_budget', 'team_size',
'implementation_timeline', 'market_share', 'competitive_position',
'content_preferences', 'consumption_patterns', 'audience_pain_points',
'buying_journey', 'seasonal_trends', 'engagement_metrics',
'top_competitors', 'competitor_content_strategies', 'market_gaps',
'industry_trends', 'emerging_trends', 'preferred_formats',
'content_mix', 'content_frequency', 'optimal_timing',
'quality_metrics', 'editorial_guidelines', 'brand_voice',
'traffic_sources', 'conversion_rates', 'content_roi_targets'
]
filled_fields = sum(1 for field in required_fields if getattr(self, field) is not None)
self.completion_percentage = (filled_fields / len(required_fields)) * 100
return self.completion_percentage
class EnhancedAIAnalysisResult(Base):
"""Enhanced AI Analysis Result model for storing comprehensive AI-generated insights."""
__tablename__ = "enhanced_ai_analysis_results"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
strategy_id = Column(Integer, ForeignKey("enhanced_content_strategies.id"), nullable=True)
# Analysis type for the 5 specialized prompts
analysis_type = Column(String(50), nullable=False) # comprehensive_strategy, audience_intelligence, competitive_intelligence, performance_optimization, content_calendar_optimization
# Comprehensive analysis results
comprehensive_insights = Column(JSON, nullable=True) # Holistic strategy insights
audience_intelligence = Column(JSON, nullable=True) # Detailed audience analysis
competitive_intelligence = Column(JSON, nullable=True) # Competitive landscape analysis
performance_optimization = Column(JSON, nullable=True) # Performance improvement recommendations
content_calendar_optimization = Column(JSON, nullable=True) # Calendar optimization insights
# Enhanced data tracking
onboarding_data_used = Column(JSON, nullable=True) # Track onboarding data integration
data_confidence_scores = Column(JSON, nullable=True) # Confidence scores for data sources
recommendation_quality_scores = Column(JSON, nullable=True) # Quality scores for recommendations
# Performance metrics
processing_time = Column(Float, nullable=True) # Processing time in seconds
ai_service_status = Column(String(20), default="operational") # operational, fallback, error
prompt_version = Column(String(50), nullable=True) # Version of AI prompt used
# Metadata
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<EnhancedAIAnalysisResult(id={self.id}, type='{self.analysis_type}', user_id={self.user_id})>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'user_id': self.user_id,
'strategy_id': self.strategy_id,
'analysis_type': self.analysis_type,
'comprehensive_insights': self.comprehensive_insights,
'audience_intelligence': self.audience_intelligence,
'competitive_intelligence': self.competitive_intelligence,
'performance_optimization': self.performance_optimization,
'content_calendar_optimization': self.content_calendar_optimization,
'onboarding_data_used': self.onboarding_data_used,
'data_confidence_scores': self.data_confidence_scores,
'recommendation_quality_scores': self.recommendation_quality_scores,
'processing_time': self.processing_time,
'ai_service_status': self.ai_service_status,
'prompt_version': self.prompt_version,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class OnboardingDataIntegration(Base):
"""Model for tracking onboarding data integration with enhanced strategy."""
__tablename__ = "onboarding_data_integrations"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, nullable=False)
strategy_id = Column(Integer, ForeignKey("enhanced_content_strategies.id"), nullable=True)
# Onboarding data sources
website_analysis_data = Column(JSON, nullable=True) # Data from website analysis
research_preferences_data = Column(JSON, nullable=True) # Data from research preferences
api_keys_data = Column(JSON, nullable=True) # API configuration data
# Integration mapping
field_mappings = Column(JSON, nullable=True) # Mapping of onboarding fields to strategy fields
auto_populated_fields = Column(JSON, nullable=True) # Fields auto-populated from onboarding
user_overrides = Column(JSON, nullable=True) # Fields manually overridden by user
# Data quality and confidence
data_quality_scores = Column(JSON, nullable=True) # Quality scores for each data source
confidence_levels = Column(JSON, nullable=True) # Confidence levels for auto-populated data
data_freshness = Column(JSON, nullable=True) # How recent the onboarding data is
# Metadata
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<OnboardingDataIntegration(id={self.id}, user_id={self.user_id}, strategy_id={self.strategy_id})>"
def to_dict(self):
"""Convert model to dictionary."""
return {
'id': self.id,
'user_id': self.user_id,
'strategy_id': self.strategy_id,
'website_analysis_data': self.website_analysis_data,
'research_preferences_data': self.research_preferences_data,
'api_keys_data': self.api_keys_data,
'field_mappings': self.field_mappings,
'auto_populated_fields': self.auto_populated_fields,
'user_overrides': self.user_overrides,
'data_quality_scores': self.data_quality_scores,
'confidence_levels': self.confidence_levels,
'data_freshness': self.data_freshness,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}

View File

@@ -0,0 +1,146 @@
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, func, JSON, Text, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
import datetime
Base = declarative_base()
class OnboardingSession(Base):
__tablename__ = 'onboarding_sessions'
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, nullable=False) # Replace with ForeignKey if you have a user table
current_step = Column(Integer, default=1)
progress = Column(Float, default=0.0)
started_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
api_keys = relationship('APIKey', back_populates='session', cascade="all, delete-orphan")
website_analyses = relationship('WebsiteAnalysis', back_populates='session', cascade="all, delete-orphan")
research_preferences = relationship('ResearchPreferences', back_populates='session', cascade="all, delete-orphan", uselist=False)
def __repr__(self):
return f"<OnboardingSession(id={self.id}, user_id={self.user_id}, step={self.current_step}, progress={self.progress})>"
class APIKey(Base):
__tablename__ = 'api_keys'
id = Column(Integer, primary_key=True, autoincrement=True)
session_id = Column(Integer, ForeignKey('onboarding_sessions.id'))
provider = Column(String(64), nullable=False)
key = Column(String(256), nullable=False)
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
session = relationship('OnboardingSession', back_populates='api_keys')
def __repr__(self):
return f"<APIKey(id={self.id}, provider={self.provider}, session_id={self.session_id})>"
def to_dict(self):
"""Convert to dictionary for API responses."""
return {
'id': self.id,
'session_id': self.session_id,
'provider': self.provider,
'key': self.key, # Note: In production, you might want to mask this
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class WebsiteAnalysis(Base):
"""Stores website analysis results from onboarding step 2."""
__tablename__ = 'website_analyses'
id = Column(Integer, primary_key=True, autoincrement=True)
session_id = Column(Integer, ForeignKey('onboarding_sessions.id', ondelete='CASCADE'), nullable=False)
website_url = Column(String(500), nullable=False)
analysis_date = Column(DateTime, default=func.now())
# Style analysis results
writing_style = Column(JSON) # Tone, voice, complexity, engagement_level
content_characteristics = Column(JSON) # Sentence structure, vocabulary, paragraph organization
target_audience = Column(JSON) # Demographics, expertise level, industry focus
content_type = Column(JSON) # Primary type, secondary types, purpose
recommended_settings = Column(JSON) # Writing tone, target audience, content type
# Crawl results
crawl_result = Column(JSON) # Raw crawl data
style_patterns = Column(JSON) # Writing patterns analysis
style_guidelines = Column(JSON) # Generated guidelines
# Metadata
status = Column(String(50), default='completed') # completed, failed, in_progress
error_message = Column(Text)
warning_message = Column(Text)
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
# Relationships
session = relationship('OnboardingSession', back_populates='website_analyses')
def __repr__(self):
return f"<WebsiteAnalysis(id={self.id}, url={self.website_url}, status={self.status})>"
def to_dict(self):
"""Convert to dictionary for API responses."""
return {
'id': self.id,
'website_url': self.website_url,
'analysis_date': self.analysis_date.isoformat() if self.analysis_date else None,
'writing_style': self.writing_style,
'content_characteristics': self.content_characteristics,
'target_audience': self.target_audience,
'content_type': self.content_type,
'recommended_settings': self.recommended_settings,
'crawl_result': self.crawl_result,
'style_patterns': self.style_patterns,
'style_guidelines': self.style_guidelines,
'status': self.status,
'error_message': self.error_message,
'warning_message': self.warning_message,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class ResearchPreferences(Base):
"""Stores research preferences from onboarding step 3."""
__tablename__ = 'research_preferences'
id = Column(Integer, primary_key=True, autoincrement=True)
session_id = Column(Integer, ForeignKey('onboarding_sessions.id', ondelete='CASCADE'), nullable=False)
# Research configuration
research_depth = Column(String(50), nullable=False) # Basic, Standard, Comprehensive, Expert
content_types = Column(JSON, nullable=False) # Array of content types
auto_research = Column(Boolean, default=True)
factual_content = Column(Boolean, default=True)
# Style detection data (from step 2)
writing_style = Column(JSON) # Tone, voice, complexity from website analysis
content_characteristics = Column(JSON) # Sentence structure, vocabulary from analysis
target_audience = Column(JSON) # Demographics, expertise level from analysis
recommended_settings = Column(JSON) # AI-generated recommendations from analysis
# Metadata
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
# Relationships
session = relationship('OnboardingSession', back_populates='research_preferences')
def __repr__(self):
return f"<ResearchPreferences(id={self.id}, session_id={self.session_id}, depth={self.research_depth})>"
def to_dict(self):
"""Convert to dictionary for API responses."""
return {
'id': self.id,
'session_id': self.session_id,
'research_depth': self.research_depth,
'content_types': self.content_types,
'auto_research': self.auto_research,
'factual_content': self.factual_content,
'writing_style': self.writing_style,
'content_characteristics': self.content_characteristics,
'target_audience': self.target_audience,
'recommended_settings': self.recommended_settings,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}

View File

@@ -0,0 +1,418 @@
"""
Database models for SEO analysis data storage
"""
from sqlalchemy import Column, Integer, String, DateTime, Text, JSON, Float, Boolean, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
from typing import Dict, Any, List
Base = declarative_base()
class SEOAnalysis(Base):
"""Main SEO analysis record"""
__tablename__ = 'seo_analyses'
id = Column(Integer, primary_key=True, index=True)
url = Column(String(500), nullable=False, index=True)
overall_score = Column(Integer, nullable=False)
health_status = Column(String(50), nullable=False) # excellent, good, needs_improvement, poor, error
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False)
analysis_data = Column(JSON, nullable=True) # Store complete analysis data
# Relationships
critical_issues = relationship("SEOIssue", back_populates="analysis", cascade="all, delete-orphan")
warnings = relationship("SEOWarning", back_populates="analysis", cascade="all, delete-orphan")
recommendations = relationship("SEORecommendation", back_populates="analysis", cascade="all, delete-orphan")
category_scores = relationship("SEOCategoryScore", back_populates="analysis", cascade="all, delete-orphan")
def __repr__(self):
return f"<SEOAnalysis(url='{self.url}', score={self.overall_score}, status='{self.health_status}')>"
class SEOIssue(Base):
"""Critical SEO issues"""
__tablename__ = 'seo_issues'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
issue_text = Column(Text, nullable=False)
category = Column(String(100), nullable=True) # url_structure, meta_data, content, etc.
priority = Column(String(20), default='critical') # critical, high, medium, low
created_at = Column(DateTime, default=datetime.utcnow)
# Relationship
analysis = relationship("SEOAnalysis", back_populates="critical_issues")
def __repr__(self):
return f"<SEOIssue(category='{self.category}', priority='{self.priority}')>"
class SEOWarning(Base):
"""SEO warnings"""
__tablename__ = 'seo_warnings'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
warning_text = Column(Text, nullable=False)
category = Column(String(100), nullable=True)
priority = Column(String(20), default='medium')
created_at = Column(DateTime, default=datetime.utcnow)
# Relationship
analysis = relationship("SEOAnalysis", back_populates="warnings")
def __repr__(self):
return f"<SEOWarning(category='{self.category}', priority='{self.priority}')>"
class SEORecommendation(Base):
"""SEO recommendations"""
__tablename__ = 'seo_recommendations'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
recommendation_text = Column(Text, nullable=False)
category = Column(String(100), nullable=True)
difficulty = Column(String(20), default='medium') # easy, medium, hard
estimated_impact = Column(String(20), default='medium') # high, medium, low
created_at = Column(DateTime, default=datetime.utcnow)
# Relationship
analysis = relationship("SEOAnalysis", back_populates="recommendations")
def __repr__(self):
return f"<SEORecommendation(category='{self.category}', difficulty='{self.difficulty}')>"
class SEOCategoryScore(Base):
"""Individual category scores"""
__tablename__ = 'seo_category_scores'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
category = Column(String(100), nullable=False) # url_structure, meta_data, content, etc.
score = Column(Integer, nullable=False)
max_score = Column(Integer, default=100)
details = Column(JSON, nullable=True) # Store category-specific details
# Relationship
analysis = relationship("SEOAnalysis", back_populates="category_scores")
def __repr__(self):
return f"<SEOCategoryScore(category='{self.category}', score={self.score})>"
class SEOAnalysisHistory(Base):
"""Historical SEO analysis data for tracking improvements"""
__tablename__ = 'seo_analysis_history'
id = Column(Integer, primary_key=True, index=True)
url = Column(String(500), nullable=False, index=True)
analysis_date = Column(DateTime, default=datetime.utcnow, nullable=False)
overall_score = Column(Integer, nullable=False)
health_status = Column(String(50), nullable=False)
score_change = Column(Integer, default=0) # Change from previous analysis
# Category scores for tracking
url_structure_score = Column(Integer, nullable=True)
meta_data_score = Column(Integer, nullable=True)
content_score = Column(Integer, nullable=True)
technical_score = Column(Integer, nullable=True)
performance_score = Column(Integer, nullable=True)
accessibility_score = Column(Integer, nullable=True)
user_experience_score = Column(Integer, nullable=True)
security_score = Column(Integer, nullable=True)
# Issue counts
critical_issues_count = Column(Integer, default=0)
warnings_count = Column(Integer, default=0)
recommendations_count = Column(Integer, default=0)
def __repr__(self):
return f"<SEOAnalysisHistory(url='{self.url}', score={self.overall_score}, date='{self.analysis_date}')>"
class SEOKeywordAnalysis(Base):
"""Keyword analysis data"""
__tablename__ = 'seo_keyword_analyses'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
keyword = Column(String(200), nullable=False)
density = Column(Float, nullable=True)
count = Column(Integer, default=0)
in_title = Column(Boolean, default=False)
in_headings = Column(Boolean, default=False)
in_alt_text = Column(Boolean, default=False)
in_meta_description = Column(Boolean, default=False)
def __repr__(self):
return f"<SEOKeywordAnalysis(keyword='{self.keyword}', density={self.density})>"
class SEOTechnicalData(Base):
"""Technical SEO data"""
__tablename__ = 'seo_technical_data'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
# Meta data
title = Column(Text, nullable=True)
title_length = Column(Integer, nullable=True)
meta_description = Column(Text, nullable=True)
meta_description_length = Column(Integer, nullable=True)
# Technical elements
has_canonical = Column(Boolean, default=False)
canonical_url = Column(String(500), nullable=True)
has_schema_markup = Column(Boolean, default=False)
schema_types = Column(JSON, nullable=True)
has_hreflang = Column(Boolean, default=False)
hreflang_data = Column(JSON, nullable=True)
# Social media
og_tags_count = Column(Integer, default=0)
twitter_tags_count = Column(Integer, default=0)
# Technical files
robots_txt_exists = Column(Boolean, default=False)
sitemap_exists = Column(Boolean, default=False)
def __repr__(self):
return f"<SEOTechnicalData(title_length={self.title_length}, has_schema={self.has_schema_markup})>"
class SEOContentData(Base):
"""Content analysis data"""
__tablename__ = 'seo_content_data'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
# Content metrics
word_count = Column(Integer, default=0)
char_count = Column(Integer, default=0)
headings_count = Column(Integer, default=0)
h1_count = Column(Integer, default=0)
h2_count = Column(Integer, default=0)
# Media
images_count = Column(Integer, default=0)
images_with_alt = Column(Integer, default=0)
images_without_alt = Column(Integer, default=0)
# Links
internal_links_count = Column(Integer, default=0)
external_links_count = Column(Integer, default=0)
# Quality metrics
readability_score = Column(Float, nullable=True)
spelling_errors = Column(Integer, default=0)
def __repr__(self):
return f"<SEOContentData(word_count={self.word_count}, readability={self.readability_score})>"
class SEOPerformanceData(Base):
"""Performance analysis data"""
__tablename__ = 'seo_performance_data'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
# Load time
load_time = Column(Float, nullable=True)
# Compression
is_compressed = Column(Boolean, default=False)
compression_type = Column(String(50), nullable=True) # gzip, br, etc.
# Caching
has_cache_headers = Column(Boolean, default=False)
cache_control = Column(String(200), nullable=True)
# HTTP headers
content_encoding = Column(String(100), nullable=True)
server_info = Column(String(200), nullable=True)
def __repr__(self):
return f"<SEOPerformanceData(load_time={self.load_time}, compressed={self.is_compressed})>"
class SEOAccessibilityData(Base):
"""Accessibility analysis data"""
__tablename__ = 'seo_accessibility_data'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
# Alt text
images_with_alt = Column(Integer, default=0)
images_without_alt = Column(Integer, default=0)
alt_text_ratio = Column(Float, nullable=True)
# Forms
form_fields_count = Column(Integer, default=0)
labeled_fields_count = Column(Integer, default=0)
label_ratio = Column(Float, nullable=True)
# ARIA
aria_elements_count = Column(Integer, default=0)
def __repr__(self):
return f"<SEOAccessibilityData(alt_ratio={self.alt_text_ratio}, aria_count={self.aria_elements_count})>"
class SEOUserExperienceData(Base):
"""User experience analysis data"""
__tablename__ = 'seo_user_experience_data'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
# Mobile
is_mobile_friendly = Column(Boolean, default=False)
has_viewport = Column(Boolean, default=False)
# CTAs
ctas_found = Column(JSON, nullable=True) # List of found CTAs
cta_count = Column(Integer, default=0)
# Navigation
has_navigation = Column(Boolean, default=False)
nav_elements_count = Column(Integer, default=0)
# Contact info
has_contact_info = Column(Boolean, default=False)
# Social media
social_links_count = Column(Integer, default=0)
social_links = Column(JSON, nullable=True)
def __repr__(self):
return f"<SEOUserExperienceData(mobile_friendly={self.is_mobile_friendly}, cta_count={self.cta_count})>"
class SEOSecurityData(Base):
"""Security headers analysis data"""
__tablename__ = 'seo_security_data'
id = Column(Integer, primary_key=True, index=True)
analysis_id = Column(Integer, ForeignKey('seo_analyses.id'), nullable=False)
# Security headers
has_x_frame_options = Column(Boolean, default=False)
has_x_content_type_options = Column(Boolean, default=False)
has_x_xss_protection = Column(Boolean, default=False)
has_strict_transport_security = Column(Boolean, default=False)
has_content_security_policy = Column(Boolean, default=False)
has_referrer_policy = Column(Boolean, default=False)
# HTTPS
is_https = Column(Boolean, default=False)
# Total security score
security_score = Column(Integer, default=0)
present_headers = Column(JSON, nullable=True)
missing_headers = Column(JSON, nullable=True)
def __repr__(self):
return f"<SEOSecurityData(score={self.security_score}, https={self.is_https})>"
# Helper functions for data conversion
def create_analysis_from_result(result: 'SEOAnalysisResult') -> SEOAnalysis:
"""Create SEOAnalysis record from analysis result"""
return SEOAnalysis(
url=result.url,
overall_score=result.overall_score,
health_status=result.health_status,
timestamp=result.timestamp,
analysis_data=result.data
)
def create_issues_from_result(analysis_id: int, result: 'SEOAnalysisResult') -> List[SEOIssue]:
"""Create SEOIssue records from analysis result"""
issues = []
for issue_data in result.critical_issues:
# Handle both string and dictionary formats
if isinstance(issue_data, dict):
issue_text = issue_data.get('message', str(issue_data))
category = issue_data.get('category', extract_category_from_text(issue_text))
else:
issue_text = str(issue_data)
category = extract_category_from_text(issue_text)
issues.append(SEOIssue(
analysis_id=analysis_id,
issue_text=issue_text,
category=category,
priority='critical'
))
return issues
def create_warnings_from_result(analysis_id: int, result: 'SEOAnalysisResult') -> List[SEOWarning]:
"""Create SEOWarning records from analysis result"""
warnings = []
for warning_data in result.warnings:
# Handle both string and dictionary formats
if isinstance(warning_data, dict):
warning_text = warning_data.get('message', str(warning_data))
category = warning_data.get('category', extract_category_from_text(warning_text))
else:
warning_text = str(warning_data)
category = extract_category_from_text(warning_text)
warnings.append(SEOWarning(
analysis_id=analysis_id,
warning_text=warning_text,
category=category,
priority='medium'
))
return warnings
def create_recommendations_from_result(analysis_id: int, result: 'SEOAnalysisResult') -> List[SEORecommendation]:
"""Create SEORecommendation records from analysis result"""
recommendations = []
for rec_data in result.recommendations:
# Handle both string and dictionary formats
if isinstance(rec_data, dict):
rec_text = rec_data.get('message', str(rec_data))
category = rec_data.get('category', extract_category_from_text(rec_text))
else:
rec_text = str(rec_data)
category = extract_category_from_text(rec_text)
recommendations.append(SEORecommendation(
analysis_id=analysis_id,
recommendation_text=rec_text,
category=category,
difficulty='medium',
estimated_impact='medium'
))
return recommendations
def create_category_scores_from_result(analysis_id: int, result: 'SEOAnalysisResult') -> List[SEOCategoryScore]:
"""Create SEOCategoryScore records from analysis result"""
scores = []
for category, data in result.data.items():
if isinstance(data, dict) and 'score' in data:
scores.append(SEOCategoryScore(
analysis_id=analysis_id,
category=category,
score=data['score'],
max_score=100,
details=data
))
return scores
def extract_category_from_text(text: str) -> str:
"""Extract category from issue/warning/recommendation text"""
text_lower = text.lower()
if any(word in text_lower for word in ['title', 'meta', 'description']):
return 'meta_data'
elif any(word in text_lower for word in ['https', 'url', 'security']):
return 'url_structure'
elif any(word in text_lower for word in ['content', 'word', 'heading', 'image']):
return 'content_analysis'
elif any(word in text_lower for word in ['schema', 'canonical', 'technical']):
return 'technical_seo'
elif any(word in text_lower for word in ['speed', 'load', 'performance']):
return 'performance'
elif any(word in text_lower for word in ['alt', 'accessibility', 'aria']):
return 'accessibility'
elif any(word in text_lower for word in ['mobile', 'cta', 'navigation']):
return 'user_experience'
else:
return 'general'