791 lines
28 KiB
Python
791 lines
28 KiB
Python
"""
|
|
Twitter Database Models for ALwrity
|
|
===================================
|
|
|
|
This module defines SQLAlchemy models for storing Twitter-related data including:
|
|
- User profiles and authentication
|
|
- Tweet content and metadata
|
|
- Analytics and engagement metrics
|
|
- Scheduling and automation data
|
|
- Performance tracking and insights
|
|
|
|
This allows the application to store Twitter data locally and reduce API calls
|
|
while providing rich analytics and historical data to users.
|
|
"""
|
|
|
|
from sqlalchemy import (
|
|
create_engine, Column, Integer, String, Text, DateTime, Boolean, Float,
|
|
Enum, ForeignKey, JSON, BigInteger, Index, UniqueConstraint
|
|
)
|
|
from sqlalchemy.orm import declarative_base, relationship, sessionmaker
|
|
from datetime import datetime, timedelta
|
|
import enum
|
|
from dataclasses import dataclass
|
|
from typing import List, Dict, Any, Optional
|
|
import json
|
|
|
|
Base = declarative_base()
|
|
|
|
# --- ENUMS ---
|
|
|
|
class TwitterAccountType(enum.Enum):
|
|
PERSONAL = "personal"
|
|
BUSINESS = "business"
|
|
CREATOR = "creator"
|
|
BRAND = "brand"
|
|
|
|
class TweetType(enum.Enum):
|
|
ORIGINAL = "original"
|
|
REPLY = "reply"
|
|
RETWEET = "retweet"
|
|
QUOTE_TWEET = "quote_tweet"
|
|
THREAD = "thread"
|
|
|
|
class TweetStatus(enum.Enum):
|
|
DRAFT = "draft"
|
|
SCHEDULED = "scheduled"
|
|
POSTED = "posted"
|
|
FAILED = "failed"
|
|
DELETED = "deleted"
|
|
|
|
class EngagementType(enum.Enum):
|
|
LIKE = "like"
|
|
RETWEET = "retweet"
|
|
REPLY = "reply"
|
|
QUOTE_TWEET = "quote_tweet"
|
|
BOOKMARK = "bookmark"
|
|
IMPRESSION = "impression"
|
|
PROFILE_CLICK = "profile_click"
|
|
URL_CLICK = "url_click"
|
|
HASHTAG_CLICK = "hashtag_click"
|
|
MENTION_CLICK = "mention_click"
|
|
|
|
class AnalyticsTimeframe(enum.Enum):
|
|
HOURLY = "hourly"
|
|
DAILY = "daily"
|
|
WEEKLY = "weekly"
|
|
MONTHLY = "monthly"
|
|
|
|
class ContentCategory(enum.Enum):
|
|
EDUCATIONAL = "educational"
|
|
PROMOTIONAL = "promotional"
|
|
PERSONAL = "personal"
|
|
NEWS = "news"
|
|
ENTERTAINMENT = "entertainment"
|
|
QUESTION = "question"
|
|
POLL = "poll"
|
|
THREAD = "thread"
|
|
|
|
# --- DATACLASSES ---
|
|
|
|
@dataclass
|
|
class TwitterCredentials:
|
|
"""Dataclass for Twitter API credentials"""
|
|
api_key: str = ""
|
|
api_secret: str = ""
|
|
access_token: str = ""
|
|
access_token_secret: str = ""
|
|
bearer_token: str = ""
|
|
|
|
def to_dict(self) -> Dict[str, str]:
|
|
return {
|
|
'api_key': self.api_key,
|
|
'api_secret': self.api_secret,
|
|
'access_token': self.access_token,
|
|
'access_token_secret': self.access_token_secret,
|
|
'bearer_token': self.bearer_token
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, str]) -> 'TwitterCredentials':
|
|
return cls(
|
|
api_key=data.get('api_key', ''),
|
|
api_secret=data.get('api_secret', ''),
|
|
access_token=data.get('access_token', ''),
|
|
access_token_secret=data.get('access_token_secret', ''),
|
|
bearer_token=data.get('bearer_token', '')
|
|
)
|
|
|
|
@dataclass
|
|
class TweetMetrics:
|
|
"""Dataclass for tweet performance metrics"""
|
|
likes: int = 0
|
|
retweets: int = 0
|
|
replies: int = 0
|
|
quotes: int = 0
|
|
bookmarks: int = 0
|
|
impressions: int = 0
|
|
profile_clicks: int = 0
|
|
url_clicks: int = 0
|
|
hashtag_clicks: int = 0
|
|
engagement_rate: float = 0.0
|
|
reach: int = 0
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return {
|
|
'likes': self.likes,
|
|
'retweets': self.retweets,
|
|
'replies': self.replies,
|
|
'quotes': self.quotes,
|
|
'bookmarks': self.bookmarks,
|
|
'impressions': self.impressions,
|
|
'profile_clicks': self.profile_clicks,
|
|
'url_clicks': self.url_clicks,
|
|
'hashtag_clicks': self.hashtag_clicks,
|
|
'engagement_rate': self.engagement_rate,
|
|
'reach': self.reach
|
|
}
|
|
|
|
# --- MODELS ---
|
|
|
|
class TwitterUser(Base):
|
|
"""
|
|
Stores Twitter user profile information and authentication data.
|
|
This reduces API calls for user profile information.
|
|
"""
|
|
__tablename__ = "twitter_users"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(String, nullable=False, unique=True) # ALwrity user ID
|
|
twitter_user_id = Column(BigInteger, nullable=False, unique=True) # Twitter user ID
|
|
username = Column(String, nullable=False, index=True) # @username
|
|
display_name = Column(String, nullable=False)
|
|
bio = Column(Text)
|
|
location = Column(String)
|
|
website = Column(String)
|
|
profile_image_url = Column(String)
|
|
banner_image_url = Column(String)
|
|
|
|
# Account metrics
|
|
followers_count = Column(Integer, default=0)
|
|
following_count = Column(Integer, default=0)
|
|
tweet_count = Column(Integer, default=0)
|
|
listed_count = Column(Integer, default=0)
|
|
|
|
# Account details
|
|
account_type = Column(Enum(TwitterAccountType), default=TwitterAccountType.PERSONAL)
|
|
verified = Column(Boolean, default=False)
|
|
protected = Column(Boolean, default=False)
|
|
created_at_twitter = Column(DateTime) # When Twitter account was created
|
|
|
|
# Authentication and API data
|
|
credentials_encrypted = Column(Text) # Encrypted JSON of TwitterCredentials
|
|
api_rate_limit_remaining = Column(Integer, default=0)
|
|
api_rate_limit_reset = Column(DateTime)
|
|
last_api_call = Column(DateTime)
|
|
|
|
# Metadata
|
|
is_active = Column(Boolean, default=True)
|
|
last_sync = Column(DateTime, default=datetime.utcnow)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
tweets = relationship("Tweet", back_populates="user", cascade="all, delete-orphan")
|
|
analytics = relationship("TwitterAnalytics", back_populates="user", cascade="all, delete-orphan")
|
|
scheduled_tweets = relationship("ScheduledTweet", back_populates="user", cascade="all, delete-orphan")
|
|
engagement_data = relationship("EngagementData", back_populates="user", cascade="all, delete-orphan")
|
|
audience_insights = relationship("AudienceInsight", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_twitter_user_username', 'username'),
|
|
Index('idx_twitter_user_sync', 'last_sync'),
|
|
Index('idx_twitter_user_active', 'is_active'),
|
|
)
|
|
|
|
class Tweet(Base):
|
|
"""
|
|
Stores tweet content, metadata, and performance data.
|
|
Includes both posted tweets and drafts.
|
|
"""
|
|
__tablename__ = "tweets"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
tweet_id = Column(BigInteger, unique=True, index=True) # Twitter tweet ID (null for drafts)
|
|
|
|
# Content
|
|
text = Column(Text, nullable=False)
|
|
hashtags = Column(JSON, default=list) # List of hashtags
|
|
mentions = Column(JSON, default=list) # List of mentioned users
|
|
urls = Column(JSON, default=list) # List of URLs in tweet
|
|
media_urls = Column(JSON, default=list) # List of media URLs
|
|
|
|
# Tweet metadata
|
|
tweet_type = Column(Enum(TweetType), default=TweetType.ORIGINAL)
|
|
status = Column(Enum(TweetStatus), default=TweetStatus.DRAFT)
|
|
category = Column(Enum(ContentCategory))
|
|
|
|
# Engagement metrics (updated periodically)
|
|
likes_count = Column(Integer, default=0)
|
|
retweets_count = Column(Integer, default=0)
|
|
replies_count = Column(Integer, default=0)
|
|
quotes_count = Column(Integer, default=0)
|
|
bookmarks_count = Column(Integer, default=0)
|
|
impressions_count = Column(Integer, default=0)
|
|
|
|
# Performance metrics
|
|
engagement_rate = Column(Float, default=0.0)
|
|
reach = Column(Integer, default=0)
|
|
click_through_rate = Column(Float, default=0.0)
|
|
|
|
# AI and generation data
|
|
ai_generated = Column(Boolean, default=False)
|
|
ai_model_used = Column(String) # Which AI model generated this
|
|
ai_prompt = Column(Text) # Original prompt used
|
|
ai_confidence_score = Column(Float) # AI confidence in content quality
|
|
generation_metadata = Column(JSON, default=dict) # Additional AI metadata
|
|
|
|
# Scheduling and posting
|
|
scheduled_for = Column(DateTime)
|
|
posted_at = Column(DateTime)
|
|
last_metrics_update = Column(DateTime)
|
|
|
|
# Thread information
|
|
thread_id = Column(String) # For grouping thread tweets
|
|
thread_position = Column(Integer) # Position in thread (1, 2, 3...)
|
|
parent_tweet_id = Column(BigInteger) # For replies
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser", back_populates="tweets")
|
|
analytics = relationship("TweetAnalytics", back_populates="tweet", cascade="all, delete-orphan")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_tweet_user_status', 'user_id', 'status'),
|
|
Index('idx_tweet_posted_at', 'posted_at'),
|
|
Index('idx_tweet_engagement', 'engagement_rate'),
|
|
Index('idx_tweet_thread', 'thread_id'),
|
|
)
|
|
|
|
class ScheduledTweet(Base):
|
|
"""
|
|
Stores scheduled tweets with automation settings.
|
|
"""
|
|
__tablename__ = "scheduled_tweets"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
tweet_id = Column(Integer, ForeignKey("tweets.id"), nullable=False)
|
|
|
|
# Scheduling details
|
|
scheduled_time = Column(DateTime, nullable=False)
|
|
timezone = Column(String, default="UTC")
|
|
recurrence_pattern = Column(String) # cron-like pattern for recurring tweets
|
|
|
|
# Automation settings
|
|
auto_optimize_time = Column(Boolean, default=False) # AI-optimize posting time
|
|
auto_add_hashtags = Column(Boolean, default=False)
|
|
auto_add_emojis = Column(Boolean, default=False)
|
|
|
|
# Status and execution
|
|
status = Column(Enum(TweetStatus), default=TweetStatus.SCHEDULED)
|
|
attempts = Column(Integer, default=0)
|
|
last_attempt = Column(DateTime)
|
|
error_message = Column(Text)
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser", back_populates="scheduled_tweets")
|
|
tweet = relationship("Tweet")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_scheduled_time', 'scheduled_time'),
|
|
Index('idx_scheduled_status', 'status'),
|
|
)
|
|
|
|
class TwitterAnalytics(Base):
|
|
"""
|
|
Stores aggregated Twitter analytics data for users.
|
|
Updated periodically to track account performance over time.
|
|
"""
|
|
__tablename__ = "twitter_analytics"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
|
|
# Time period
|
|
date = Column(DateTime, nullable=False)
|
|
timeframe = Column(Enum(AnalyticsTimeframe), nullable=False)
|
|
|
|
# Account metrics
|
|
followers_gained = Column(Integer, default=0)
|
|
followers_lost = Column(Integer, default=0)
|
|
net_follower_change = Column(Integer, default=0)
|
|
following_change = Column(Integer, default=0)
|
|
|
|
# Content metrics
|
|
tweets_posted = Column(Integer, default=0)
|
|
total_impressions = Column(Integer, default=0)
|
|
total_engagements = Column(Integer, default=0)
|
|
total_likes = Column(Integer, default=0)
|
|
total_retweets = Column(Integer, default=0)
|
|
total_replies = Column(Integer, default=0)
|
|
total_quotes = Column(Integer, default=0)
|
|
|
|
# Performance metrics
|
|
average_engagement_rate = Column(Float, default=0.0)
|
|
top_tweet_id = Column(BigInteger) # Best performing tweet
|
|
top_tweet_engagement = Column(Integer, default=0)
|
|
|
|
# Audience metrics
|
|
profile_visits = Column(Integer, default=0)
|
|
mention_count = Column(Integer, default=0)
|
|
hashtag_performance = Column(JSON, default=dict) # Top hashtags and their performance
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser", back_populates="analytics")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_analytics_user_date', 'user_id', 'date'),
|
|
Index('idx_analytics_timeframe', 'timeframe'),
|
|
UniqueConstraint('user_id', 'date', 'timeframe', name='uq_user_date_timeframe'),
|
|
)
|
|
|
|
class TweetAnalytics(Base):
|
|
"""
|
|
Stores detailed analytics for individual tweets.
|
|
Updated periodically to track tweet performance over time.
|
|
"""
|
|
__tablename__ = "tweet_analytics"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
tweet_id = Column(Integer, ForeignKey("tweets.id"), nullable=False)
|
|
|
|
# Time period
|
|
recorded_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
|
|
# Engagement metrics
|
|
likes = Column(Integer, default=0)
|
|
retweets = Column(Integer, default=0)
|
|
replies = Column(Integer, default=0)
|
|
quotes = Column(Integer, default=0)
|
|
bookmarks = Column(Integer, default=0)
|
|
|
|
# Reach metrics
|
|
impressions = Column(Integer, default=0)
|
|
reach = Column(Integer, default=0)
|
|
profile_clicks = Column(Integer, default=0)
|
|
|
|
# Click metrics
|
|
url_clicks = Column(Integer, default=0)
|
|
hashtag_clicks = Column(Integer, default=0)
|
|
mention_clicks = Column(Integer, default=0)
|
|
media_views = Column(Integer, default=0)
|
|
|
|
# Calculated metrics
|
|
engagement_rate = Column(Float, default=0.0)
|
|
click_through_rate = Column(Float, default=0.0)
|
|
virality_score = Column(Float, default=0.0) # Custom metric for viral potential
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
# Relationships
|
|
tweet = relationship("Tweet", back_populates="analytics")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_tweet_analytics_recorded', 'recorded_at'),
|
|
Index('idx_tweet_analytics_engagement', 'engagement_rate'),
|
|
)
|
|
|
|
class EngagementData(Base):
|
|
"""
|
|
Stores individual engagement events for detailed analysis.
|
|
"""
|
|
__tablename__ = "engagement_data"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
tweet_id = Column(Integer, ForeignKey("tweets.id"))
|
|
|
|
# Engagement details
|
|
engagement_type = Column(Enum(EngagementType), nullable=False)
|
|
engaging_user_id = Column(BigInteger) # Twitter ID of user who engaged
|
|
engaging_username = Column(String)
|
|
|
|
# Metadata
|
|
occurred_at = Column(DateTime, nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser", back_populates="engagement_data")
|
|
tweet = relationship("Tweet")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_engagement_user_type', 'user_id', 'engagement_type'),
|
|
Index('idx_engagement_occurred', 'occurred_at'),
|
|
)
|
|
|
|
class AudienceInsight(Base):
|
|
"""
|
|
Stores audience demographics and behavior insights.
|
|
"""
|
|
__tablename__ = "audience_insights"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
|
|
# Time period
|
|
date = Column(DateTime, nullable=False)
|
|
|
|
# Demographics (aggregated data)
|
|
top_locations = Column(JSON, default=list) # Top follower locations
|
|
age_demographics = Column(JSON, default=dict) # Age distribution
|
|
gender_demographics = Column(JSON, default=dict) # Gender distribution
|
|
language_demographics = Column(JSON, default=dict) # Language distribution
|
|
|
|
# Behavior insights
|
|
most_active_hours = Column(JSON, default=list) # When audience is most active
|
|
top_interests = Column(JSON, default=list) # Audience interests
|
|
engagement_patterns = Column(JSON, default=dict) # How audience engages
|
|
|
|
# Content preferences
|
|
preferred_content_types = Column(JSON, default=dict)
|
|
top_hashtags_used = Column(JSON, default=list)
|
|
response_rate_by_content = Column(JSON, default=dict)
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser", back_populates="audience_insights")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_audience_user_date', 'user_id', 'date'),
|
|
)
|
|
|
|
class HashtagPerformance(Base):
|
|
"""
|
|
Tracks performance of hashtags used by the user.
|
|
"""
|
|
__tablename__ = "hashtag_performance"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
|
|
# Hashtag details
|
|
hashtag = Column(String, nullable=False, index=True)
|
|
usage_count = Column(Integer, default=0)
|
|
|
|
# Performance metrics
|
|
total_impressions = Column(Integer, default=0)
|
|
total_engagements = Column(Integer, default=0)
|
|
average_engagement_rate = Column(Float, default=0.0)
|
|
|
|
# Best performing tweet with this hashtag
|
|
best_tweet_id = Column(Integer, ForeignKey("tweets.id"))
|
|
best_tweet_engagement = Column(Integer, default=0)
|
|
|
|
# Time tracking
|
|
first_used = Column(DateTime)
|
|
last_used = Column(DateTime)
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser")
|
|
best_tweet = relationship("Tweet")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_hashtag_user_performance', 'user_id', 'average_engagement_rate'),
|
|
UniqueConstraint('user_id', 'hashtag', name='uq_user_hashtag'),
|
|
)
|
|
|
|
class ContentTemplate(Base):
|
|
"""
|
|
Stores reusable tweet templates and AI prompts.
|
|
"""
|
|
__tablename__ = "content_templates"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False)
|
|
|
|
# Template details
|
|
name = Column(String, nullable=False)
|
|
description = Column(Text)
|
|
template_text = Column(Text, nullable=False)
|
|
category = Column(Enum(ContentCategory))
|
|
|
|
# Template variables and settings
|
|
variables = Column(JSON, default=list) # List of template variables
|
|
default_hashtags = Column(JSON, default=list)
|
|
suggested_times = Column(JSON, default=list) # Best times to post this type
|
|
|
|
# AI settings
|
|
ai_prompt = Column(Text) # AI prompt for generating content
|
|
ai_tone = Column(String) # Tone for AI generation
|
|
ai_target_audience = Column(String)
|
|
|
|
# Usage tracking
|
|
usage_count = Column(Integer, default=0)
|
|
last_used = Column(DateTime)
|
|
average_performance = Column(Float, default=0.0)
|
|
|
|
# Metadata
|
|
is_active = Column(Boolean, default=True)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser")
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
Index('idx_template_user_category', 'user_id', 'category'),
|
|
Index('idx_template_performance', 'average_performance'),
|
|
)
|
|
|
|
class TwitterSettings(Base):
|
|
"""
|
|
Stores user-specific Twitter settings and preferences.
|
|
"""
|
|
__tablename__ = "twitter_settings"
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
user_id = Column(Integer, ForeignKey("twitter_users.id"), nullable=False, unique=True)
|
|
|
|
# Posting preferences
|
|
default_hashtags = Column(JSON, default=list)
|
|
auto_add_hashtags = Column(Boolean, default=False)
|
|
auto_add_emojis = Column(Boolean, default=False)
|
|
max_hashtags_per_tweet = Column(Integer, default=2)
|
|
|
|
# Scheduling preferences
|
|
preferred_posting_times = Column(JSON, default=list)
|
|
timezone = Column(String, default="UTC")
|
|
auto_optimize_timing = Column(Boolean, default=False)
|
|
|
|
# AI preferences
|
|
ai_tone_preference = Column(String, default="casual")
|
|
ai_target_audience = Column(String, default="general")
|
|
ai_creativity_level = Column(Float, default=0.7) # 0-1 scale
|
|
|
|
# Analytics preferences
|
|
analytics_frequency = Column(String, default="daily") # hourly, daily, weekly
|
|
track_competitor_hashtags = Column(JSON, default=list)
|
|
notification_preferences = Column(JSON, default=dict)
|
|
|
|
# Content preferences
|
|
content_categories = Column(JSON, default=list) # Preferred content types
|
|
avoid_topics = Column(JSON, default=list) # Topics to avoid
|
|
brand_keywords = Column(JSON, default=list) # Brand-related keywords
|
|
|
|
# Automation settings
|
|
auto_retweet_keywords = Column(JSON, default=list)
|
|
auto_like_keywords = Column(JSON, default=list)
|
|
auto_follow_back = Column(Boolean, default=False)
|
|
|
|
# Metadata
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# Relationships
|
|
user = relationship("TwitterUser")
|
|
|
|
# --- DATABASE FUNCTIONS ---
|
|
|
|
def get_twitter_engine(db_url: str = "sqlite:///twitter_data.db"):
|
|
"""Create and return database engine for Twitter data."""
|
|
return create_engine(db_url, echo=False)
|
|
|
|
def init_twitter_db(engine):
|
|
"""Initialize Twitter database tables."""
|
|
Base.metadata.create_all(engine)
|
|
|
|
def get_twitter_session(engine):
|
|
"""Create and return database session for Twitter data."""
|
|
Session = sessionmaker(bind=engine)
|
|
return Session()
|
|
|
|
def create_twitter_user(session, user_data: Dict[str, Any]) -> TwitterUser:
|
|
"""Create a new Twitter user record."""
|
|
twitter_user = TwitterUser(
|
|
user_id=user_data['user_id'],
|
|
twitter_user_id=user_data['twitter_user_id'],
|
|
username=user_data['username'],
|
|
display_name=user_data['display_name'],
|
|
bio=user_data.get('bio', ''),
|
|
location=user_data.get('location', ''),
|
|
website=user_data.get('website', ''),
|
|
profile_image_url=user_data.get('profile_image_url', ''),
|
|
banner_image_url=user_data.get('banner_image_url', ''),
|
|
followers_count=user_data.get('followers_count', 0),
|
|
following_count=user_data.get('following_count', 0),
|
|
tweet_count=user_data.get('tweet_count', 0),
|
|
verified=user_data.get('verified', False),
|
|
protected=user_data.get('protected', False),
|
|
created_at_twitter=user_data.get('created_at_twitter'),
|
|
credentials_encrypted=user_data.get('credentials_encrypted', ''),
|
|
)
|
|
|
|
session.add(twitter_user)
|
|
session.commit()
|
|
return twitter_user
|
|
|
|
def update_user_metrics(session, user_id: int, metrics: Dict[str, Any]):
|
|
"""Update user metrics from Twitter API."""
|
|
user = session.query(TwitterUser).filter_by(id=user_id).first()
|
|
if user:
|
|
user.followers_count = metrics.get('followers_count', user.followers_count)
|
|
user.following_count = metrics.get('following_count', user.following_count)
|
|
user.tweet_count = metrics.get('tweet_count', user.tweet_count)
|
|
user.last_sync = datetime.utcnow()
|
|
session.commit()
|
|
|
|
def create_tweet_record(session, tweet_data: Dict[str, Any]) -> Tweet:
|
|
"""Create a new tweet record."""
|
|
# Handle both 'text' and 'content' field names for compatibility
|
|
text_content = tweet_data.get('text') or tweet_data.get('content')
|
|
if not text_content:
|
|
raise ValueError("Tweet must have either 'text' or 'content' field")
|
|
|
|
tweet = Tweet(
|
|
user_id=tweet_data['user_id'],
|
|
tweet_id=tweet_data.get('tweet_id'),
|
|
text=text_content,
|
|
hashtags=tweet_data.get('hashtags', []),
|
|
mentions=tweet_data.get('mentions', []),
|
|
urls=tweet_data.get('urls', []),
|
|
media_urls=tweet_data.get('media_urls', []),
|
|
tweet_type=TweetType(tweet_data.get('tweet_type', 'original')),
|
|
status=TweetStatus(tweet_data.get('status', 'draft')),
|
|
category=ContentCategory(tweet_data['category']) if tweet_data.get('category') else None,
|
|
ai_generated=tweet_data.get('ai_generated', False),
|
|
ai_model_used=tweet_data.get('ai_model_used'),
|
|
ai_prompt=tweet_data.get('ai_prompt'),
|
|
ai_confidence_score=tweet_data.get('ai_confidence_score'),
|
|
generation_metadata=tweet_data.get('generation_metadata', {}),
|
|
scheduled_for=tweet_data.get('scheduled_for'),
|
|
posted_at=tweet_data.get('posted_at'),
|
|
thread_id=tweet_data.get('thread_id'),
|
|
thread_position=tweet_data.get('thread_position'),
|
|
parent_tweet_id=tweet_data.get('parent_tweet_id'),
|
|
)
|
|
|
|
session.add(tweet)
|
|
session.commit()
|
|
return tweet
|
|
|
|
def update_tweet_metrics(session, tweet_id: int, metrics: TweetMetrics):
|
|
"""Update tweet metrics from Twitter API."""
|
|
tweet = session.query(Tweet).filter_by(id=tweet_id).first()
|
|
if tweet:
|
|
tweet.likes_count = metrics.likes
|
|
tweet.retweets_count = metrics.retweets
|
|
tweet.replies_count = metrics.replies
|
|
tweet.quotes_count = metrics.quotes
|
|
tweet.bookmarks_count = metrics.bookmarks
|
|
tweet.impressions_count = metrics.impressions
|
|
tweet.engagement_rate = metrics.engagement_rate
|
|
tweet.reach = metrics.reach
|
|
tweet.last_metrics_update = datetime.utcnow()
|
|
session.commit()
|
|
|
|
# Also create analytics record
|
|
analytics = TweetAnalytics(
|
|
tweet_id=tweet_id,
|
|
likes=metrics.likes,
|
|
retweets=metrics.retweets,
|
|
replies=metrics.replies,
|
|
quotes=metrics.quotes,
|
|
bookmarks=metrics.bookmarks,
|
|
impressions=metrics.impressions,
|
|
reach=metrics.reach,
|
|
engagement_rate=metrics.engagement_rate,
|
|
click_through_rate=metrics.url_clicks / max(metrics.impressions, 1) * 100,
|
|
virality_score=calculate_virality_score(metrics)
|
|
)
|
|
session.add(analytics)
|
|
session.commit()
|
|
|
|
def calculate_virality_score(metrics: TweetMetrics) -> float:
|
|
"""Calculate a custom virality score based on engagement metrics."""
|
|
if metrics.impressions == 0:
|
|
return 0.0
|
|
|
|
# Weight different engagement types
|
|
engagement_score = (
|
|
metrics.likes * 1.0 +
|
|
metrics.retweets * 3.0 + # Retweets are more valuable
|
|
metrics.replies * 2.0 +
|
|
metrics.quotes * 2.5 +
|
|
metrics.bookmarks * 1.5
|
|
)
|
|
|
|
# Normalize by impressions and scale
|
|
virality = (engagement_score / metrics.impressions) * 100
|
|
return min(virality, 100.0) # Cap at 100
|
|
|
|
def get_user_analytics_summary(session, user_id: int, days: int = 30) -> Dict[str, Any]:
|
|
"""Get analytics summary for a user over specified days."""
|
|
from sqlalchemy import func
|
|
|
|
start_date = datetime.utcnow() - timedelta(days=days)
|
|
|
|
# Get tweet metrics
|
|
tweet_stats = session.query(
|
|
func.count(Tweet.id).label('total_tweets'),
|
|
func.avg(Tweet.engagement_rate).label('avg_engagement'),
|
|
func.sum(Tweet.likes_count).label('total_likes'),
|
|
func.sum(Tweet.retweets_count).label('total_retweets'),
|
|
func.sum(Tweet.impressions_count).label('total_impressions')
|
|
).filter(
|
|
Tweet.user_id == user_id,
|
|
Tweet.posted_at >= start_date,
|
|
Tweet.status == TweetStatus.POSTED
|
|
).first()
|
|
|
|
# Get follower growth
|
|
user = session.query(TwitterUser).filter_by(id=user_id).first()
|
|
|
|
return {
|
|
'total_tweets': tweet_stats.total_tweets or 0,
|
|
'average_engagement_rate': float(tweet_stats.avg_engagement or 0),
|
|
'total_likes': tweet_stats.total_likes or 0,
|
|
'total_retweets': tweet_stats.total_retweets or 0,
|
|
'total_impressions': tweet_stats.total_impressions or 0,
|
|
'current_followers': user.followers_count if user else 0,
|
|
'period_days': days
|
|
}
|
|
|
|
# Export all models and functions
|
|
__all__ = [
|
|
# Models
|
|
'TwitterUser', 'Tweet', 'ScheduledTweet', 'TwitterAnalytics', 'TweetAnalytics',
|
|
'EngagementData', 'AudienceInsight', 'HashtagPerformance', 'ContentTemplate',
|
|
'TwitterSettings',
|
|
|
|
# Enums
|
|
'TwitterAccountType', 'TweetType', 'TweetStatus', 'EngagementType',
|
|
'AnalyticsTimeframe', 'ContentCategory',
|
|
|
|
# Dataclasses
|
|
'TwitterCredentials', 'TweetMetrics',
|
|
|
|
# Functions
|
|
'get_twitter_engine', 'init_twitter_db', 'get_twitter_session',
|
|
'create_twitter_user', 'update_user_metrics', 'create_tweet_record',
|
|
'update_tweet_metrics', 'calculate_virality_score', 'get_user_analytics_summary'
|
|
] |