Initial commit
This commit is contained in:
15
backend/app/models/__init__.py
Normal file
15
backend/app/models/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""Modèles SQLAlchemy de l'application."""
|
||||
|
||||
from .user import User
|
||||
from .tweet import Tweet
|
||||
from .reddit_post import RedditPost, RedditComment
|
||||
from .rss_article import RSSArticle
|
||||
from .sentiment_score import SentimentScore
|
||||
from .energy_score import EnergyScore
|
||||
from .match import Match
|
||||
from .prediction import Prediction
|
||||
from .user_prediction import UserPrediction
|
||||
from .badge import Badge, UserBadge
|
||||
from .api_key import ApiKey
|
||||
|
||||
__all__ = ["User", "Tweet", "RedditPost", "RedditComment", "RSSArticle", "SentimentScore", "EnergyScore", "Match", "Prediction", "UserPrediction", "Badge", "UserBadge", "ApiKey"]
|
||||
55
backend/app/models/api_key.py
Normal file
55
backend/app/models/api_key.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
SQLAlchemy model for API keys.
|
||||
|
||||
This module defines the database model for storing API keys used for public API authentication.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class ApiKey(Base):
|
||||
"""
|
||||
Model for storing user API keys.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
user_id: Foreign key to users table
|
||||
key_hash: Hashed API key (never store plain keys)
|
||||
key_prefix: First 8 characters of key for identification
|
||||
is_active: Whether the key is active
|
||||
rate_limit: Rate limit per minute for this key
|
||||
last_used_at: Timestamp of last API usage
|
||||
created_at: Timestamp when key was created
|
||||
"""
|
||||
__tablename__ = "api_keys"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
key_hash = Column(String(255), nullable=False, unique=True, index=True)
|
||||
key_prefix = Column(String(8), nullable=False, index=True)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
rate_limit = Column(Integer, default=100, nullable=False) # Default: 100 req/min
|
||||
last_used_at = Column(DateTime, nullable=True)
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="api_keys")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<ApiKey(id={self.id}, user_id={self.user_id}, prefix={self.key_prefix})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert API key model to dictionary (safe version)."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'user_id': self.user_id,
|
||||
'key_prefix': self.key_prefix,
|
||||
'is_active': self.is_active,
|
||||
'rate_limit': self.rate_limit,
|
||||
'last_used_at': self.last_used_at.isoformat() if self.last_used_at else None,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None
|
||||
}
|
||||
45
backend/app/models/badge.py
Normal file
45
backend/app/models/badge.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Badge models for SQLAlchemy ORM
|
||||
"""
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Badge(Base):
|
||||
"""Badge definition model"""
|
||||
__tablename__ = "badges"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
badge_id = Column(String(50), unique=True, nullable=False, index=True)
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(Text, nullable=False)
|
||||
icon = Column(String(10), nullable=False) # emoji or icon string
|
||||
category = Column(String(50), nullable=False) # predictions, accuracy, engagement, social
|
||||
criteria_type = Column(String(50), nullable=False) # predictions_count, correct_predictions, etc.
|
||||
criteria_value = Column(Integer, nullable=False)
|
||||
criteria_description = Column(String(200), nullable=False)
|
||||
rarity = Column(String(20), nullable=False) # common, rare, epic, legendary
|
||||
points = Column(Integer, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
||||
# Relationships
|
||||
user_badges = relationship("UserBadge", back_populates="badge")
|
||||
|
||||
|
||||
class UserBadge(Base):
|
||||
"""User's unlocked badges"""
|
||||
__tablename__ = "user_badges"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||
badge_id = Column(Integer, ForeignKey("badges.id"), nullable=False, index=True)
|
||||
unlocked_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="badges")
|
||||
badge = relationship("Badge", back_populates="user_badges")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<UserBadge user_id={self.user_id} badge_id={self.badge_id}>"
|
||||
101
backend/app/models/energy_score.py
Normal file
101
backend/app/models/energy_score.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
SQLAlchemy model for energy scores.
|
||||
|
||||
This module defines the database model for storing energy score calculations
|
||||
from multiple sources (Twitter, Reddit, RSS).
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Index, ForeignKey, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class EnergyScore(Base):
|
||||
"""
|
||||
Model for storing energy score calculations.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
match_id: Foreign key to the match
|
||||
team_id: Foreign key to the team
|
||||
score: Final energy score (0-100)
|
||||
confidence: Confidence level (0-1)
|
||||
sources_used: List of sources used in calculation (JSON)
|
||||
twitter_score: Energy score from Twitter component
|
||||
reddit_score: Energy score from Reddit component
|
||||
rss_score: Energy score from RSS component
|
||||
temporal_factor: Temporal weighting factor applied
|
||||
twitter_weight: Adjusted weight for Twitter (in degraded mode)
|
||||
reddit_weight: Adjusted weight for Reddit (in degraded mode)
|
||||
rss_weight: Adjusted weight for RSS (in degraded mode)
|
||||
created_at: Timestamp when the energy score was calculated
|
||||
updated_at: Timestamp when the energy score was last updated
|
||||
"""
|
||||
__tablename__ = "energy_scores"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
match_id = Column(Integer, nullable=False, index=True, comment="ID of the match")
|
||||
team_id = Column(Integer, nullable=False, index=True, comment="ID of the team")
|
||||
score = Column(Float, nullable=False, index=True, comment="Final energy score (0-100)")
|
||||
confidence = Column(Float, nullable=False, default=0.0, comment="Confidence level (0-1)")
|
||||
|
||||
# Sources used (stored as JSON array)
|
||||
sources_used = Column(JSON, nullable=False, default=list, comment="List of sources used")
|
||||
|
||||
# Component scores
|
||||
twitter_score = Column(Float, nullable=True, comment="Energy score from Twitter component")
|
||||
reddit_score = Column(Float, nullable=True, comment="Energy score from Reddit component")
|
||||
rss_score = Column(Float, nullable=True, comment="Energy score from RSS component")
|
||||
|
||||
# Temporal weighting
|
||||
temporal_factor = Column(Float, nullable=True, comment="Temporal weighting factor applied")
|
||||
|
||||
# Adjusted weights (for degraded mode tracking)
|
||||
twitter_weight = Column(Float, nullable=True, comment="Adjusted weight for Twitter")
|
||||
reddit_weight = Column(Float, nullable=True, comment="Adjusted weight for Reddit")
|
||||
rss_weight = Column(Float, nullable=True, comment="Adjusted weight for RSS")
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, nullable=False, index=True, default=datetime.utcnow, comment="Creation timestamp")
|
||||
updated_at = Column(DateTime, nullable=False, index=True, default=datetime.utcnow, onupdate=datetime.utcnow, comment="Last update timestamp")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_energy_scores_match_team', 'match_id', 'team_id'),
|
||||
Index('idx_energy_scores_score', 'score'),
|
||||
Index('idx_energy_scores_confidence', 'confidence'),
|
||||
Index('idx_energy_scores_created_at', 'created_at'),
|
||||
Index('idx_energy_scores_updated_at', 'updated_at'),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (f"<EnergyScore(id={self.id}, match_id={self.match_id}, "
|
||||
f"team_id={self.team_id}, score={self.score}, "
|
||||
f"confidence={self.confidence})>")
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""
|
||||
Convert energy score model to dictionary.
|
||||
|
||||
Returns:
|
||||
Dictionary representation of the energy score
|
||||
"""
|
||||
return {
|
||||
'id': self.id,
|
||||
'match_id': self.match_id,
|
||||
'team_id': self.team_id,
|
||||
'score': self.score,
|
||||
'confidence': self.confidence,
|
||||
'sources_used': self.sources_used,
|
||||
'twitter_score': self.twitter_score,
|
||||
'reddit_score': self.reddit_score,
|
||||
'rss_score': self.rss_score,
|
||||
'temporal_factor': self.temporal_factor,
|
||||
'twitter_weight': self.twitter_weight,
|
||||
'reddit_weight': self.reddit_weight,
|
||||
'rss_weight': self.rss_weight,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
'updated_at': self.updated_at.isoformat() if self.updated_at else None
|
||||
}
|
||||
62
backend/app/models/match.py
Normal file
62
backend/app/models/match.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
SQLAlchemy model for matches.
|
||||
|
||||
This module defines the database model for storing match information.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Index
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Match(Base):
|
||||
"""
|
||||
Model for storing match information.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
home_team: Name of the home team
|
||||
away_team: Name of the away team
|
||||
date: Match date and time
|
||||
league: League name
|
||||
status: Match status (scheduled, in_progress, completed, etc.)
|
||||
"""
|
||||
__tablename__ = "matches"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
home_team = Column(String(255), nullable=False, index=True)
|
||||
away_team = Column(String(255), nullable=False, index=True)
|
||||
date = Column(DateTime, nullable=False, index=True)
|
||||
league = Column(String(255), nullable=False, index=True)
|
||||
status = Column(String(50), nullable=False, index=True)
|
||||
actual_winner = Column(String(255), nullable=True, index=True, comment='Actual winner: home, away, or draw')
|
||||
|
||||
# Relationships
|
||||
predictions = relationship("Prediction", back_populates="match", cascade="all, delete-orphan")
|
||||
tweets = relationship("Tweet", back_populates="match", cascade="all, delete-orphan")
|
||||
posts_reddit = relationship("RedditPost", back_populates="match", cascade="all, delete-orphan")
|
||||
rss_articles = relationship("RSSArticle", back_populates="match", cascade="all, delete-orphan")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_matches_date_league', 'date', 'league'),
|
||||
Index('idx_matches_home_away', 'home_team', 'away_team'),
|
||||
Index('idx_matches_actual_winner', 'actual_winner'),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Match(id={self.id}, {self.home_team} vs {self.away_team}, date={self.date})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert match model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'home_team': self.home_team,
|
||||
'away_team': self.away_team,
|
||||
'date': self.date.isoformat() if self.date else None,
|
||||
'league': self.league,
|
||||
'status': self.status,
|
||||
'actual_winner': self.actual_winner
|
||||
}
|
||||
57
backend/app/models/prediction.py
Normal file
57
backend/app/models/prediction.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
SQLAlchemy model for predictions.
|
||||
|
||||
This module defines the database model for storing match predictions.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Index
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Prediction(Base):
|
||||
"""
|
||||
Model for storing match predictions.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
match_id: Foreign key to matches table
|
||||
energy_score: Energy score for the prediction
|
||||
confidence: Confidence level of the prediction
|
||||
predicted_winner: Predicted winner team name
|
||||
created_at: Timestamp when prediction was created
|
||||
"""
|
||||
__tablename__ = "predictions"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
match_id = Column(Integer, ForeignKey("matches.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
energy_score = Column(String(50), nullable=False)
|
||||
confidence = Column(String(50), nullable=False)
|
||||
predicted_winner = Column(String(255), nullable=False)
|
||||
created_at = Column(DateTime, nullable=False, index=True)
|
||||
|
||||
# Relationships
|
||||
match = relationship("Match", back_populates="predictions")
|
||||
user_predictions = relationship("UserPrediction", back_populates="prediction", cascade="all, delete-orphan")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_predictions_match_id_created', 'match_id', 'created_at'),
|
||||
Index('idx_predictions_confidence', 'confidence'),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Prediction(id={self.id}, match_id={self.match_id}, confidence={self.confidence})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert prediction model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'match_id': self.match_id,
|
||||
'energy_score': self.energy_score,
|
||||
'confidence': self.confidence,
|
||||
'predicted_winner': self.predicted_winner,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None
|
||||
}
|
||||
118
backend/app/models/reddit_post.py
Normal file
118
backend/app/models/reddit_post.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
SQLAlchemy model for Reddit posts.
|
||||
|
||||
This module defines database models for storing posts and comments
|
||||
collected from Reddit API.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Index, Text, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class RedditPost(Base):
|
||||
"""
|
||||
Model for storing Reddit posts about football matches.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
post_id: Unique identifier from Reddit
|
||||
title: Post title
|
||||
text: Post content
|
||||
upvotes: Number of upvotes
|
||||
created_at: Timestamp when post was created
|
||||
match_id: Foreign key to matches table
|
||||
subreddit: Subreddit name
|
||||
source: Source platform (reddit)
|
||||
"""
|
||||
__tablename__ = "posts_reddit"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
post_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
title = Column(String(500), nullable=False)
|
||||
text = Column(Text, nullable=True)
|
||||
upvotes = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, nullable=False, index=True)
|
||||
match_id = Column(Integer, ForeignKey('matches.id'), nullable=True, index=True)
|
||||
subreddit = Column(String(100), nullable=False)
|
||||
source = Column(String(50), default="reddit")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_posts_reddit_match_id', 'match_id'),
|
||||
Index('idx_posts_reddit_created_at', 'created_at'),
|
||||
Index('idx_posts_reddit_subreddit', 'subreddit'),
|
||||
)
|
||||
|
||||
# Relationship with match
|
||||
match = relationship("Match", back_populates="posts_reddit")
|
||||
|
||||
# Relationship with comments
|
||||
comments = relationship("RedditComment", back_populates="post", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<RedditPost(id={self.id}, post_id={self.post_id}, subreddit={self.subreddit})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert Reddit post model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'post_id': self.post_id,
|
||||
'title': self.title,
|
||||
'text': self.text,
|
||||
'upvotes': self.upvotes,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
'match_id': self.match_id,
|
||||
'subreddit': self.subreddit,
|
||||
'source': self.source
|
||||
}
|
||||
|
||||
|
||||
class RedditComment(Base):
|
||||
"""
|
||||
Model for storing Reddit comments.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
comment_id: Unique identifier from Reddit
|
||||
post_id: Foreign key to posts_reddit table
|
||||
text: Comment content
|
||||
upvotes: Number of upvotes
|
||||
created_at: Timestamp when comment was created
|
||||
source: Source platform (reddit)
|
||||
"""
|
||||
__tablename__ = "comments_reddit"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
comment_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
post_id = Column(Integer, ForeignKey('posts_reddit.id'), nullable=False, index=True)
|
||||
text = Column(Text, nullable=False)
|
||||
upvotes = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, nullable=False, index=True)
|
||||
source = Column(String(50), default="reddit")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_comments_reddit_post_id', 'post_id'),
|
||||
Index('idx_comments_reddit_created_at', 'created_at'),
|
||||
)
|
||||
|
||||
# Relationship with post
|
||||
post = relationship("RedditPost", back_populates="comments")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<RedditComment(id={self.id}, comment_id={self.comment_id}, post_id={self.post_id})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert Reddit comment model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'comment_id': self.comment_id,
|
||||
'post_id': self.post_id,
|
||||
'text': self.text,
|
||||
'upvotes': self.upvotes,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
'source': self.source
|
||||
}
|
||||
64
backend/app/models/rss_article.py
Normal file
64
backend/app/models/rss_article.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
SQLAlchemy model for RSS articles.
|
||||
|
||||
This module defines the database model for storing RSS articles
|
||||
collected from various RSS feeds.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Text, Index, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class RSSArticle(Base):
|
||||
"""
|
||||
Model for storing RSS articles collected from sports feeds.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
article_id: Unique identifier from RSS feed
|
||||
title: Article title
|
||||
content: Article content/description
|
||||
published_at: Timestamp when article was published
|
||||
source_url: URL of the RSS feed source
|
||||
match_id: Foreign key to matches table
|
||||
source: Source feed name (ESPN, BBC Sport, etc.)
|
||||
"""
|
||||
__tablename__ = "rss_articles"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
article_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
title = Column(String(500), nullable=False)
|
||||
content = Column(Text, nullable=True)
|
||||
published_at = Column(DateTime, nullable=False, index=True)
|
||||
source_url = Column(String(1000), nullable=False)
|
||||
match_id = Column(Integer, ForeignKey('matches.id'), nullable=True, index=True)
|
||||
source = Column(String(100), default="rss")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_rss_articles_match_id_source', 'match_id', 'source'),
|
||||
Index('idx_rss_articles_published_at', 'published_at'),
|
||||
Index('idx_rss_articles_source_url', 'source_url'),
|
||||
)
|
||||
|
||||
# Relationship with match
|
||||
match = relationship("Match", back_populates="rss_articles")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<RSSArticle(id={self.id}, article_id={self.article_id}, match_id={self.match_id})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert RSS article model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'article_id': self.article_id,
|
||||
'title': self.title,
|
||||
'content': self.content,
|
||||
'published_at': self.published_at.isoformat() if self.published_at else None,
|
||||
'source_url': self.source_url,
|
||||
'match_id': self.match_id,
|
||||
'source': self.source
|
||||
}
|
||||
65
backend/app/models/sentiment_score.py
Normal file
65
backend/app/models/sentiment_score.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
SQLAlchemy model for sentiment scores.
|
||||
|
||||
This module defines the database model for storing sentiment analysis results
|
||||
from tweets and posts.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Index, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class SentimentScore(Base):
|
||||
"""
|
||||
Model for storing sentiment analysis results.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
entity_id: Foreign key to the entity being analyzed (tweet_id or post_id)
|
||||
entity_type: Type of entity ('tweet' or 'reddit_post')
|
||||
score: Overall compound sentiment score (-1 to 1)
|
||||
sentiment_type: Classification ('positive', 'negative', or 'neutral')
|
||||
positive: Positive proportion score (0 to 1)
|
||||
negative: Negative proportion score (0 to 1)
|
||||
neutral: Neutral proportion score (0 to 1)
|
||||
created_at: Timestamp when the sentiment was analyzed
|
||||
"""
|
||||
__tablename__ = "sentiment_scores"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
entity_id = Column(String(255), nullable=False, index=True)
|
||||
entity_type = Column(String(50), nullable=False, index=True) # 'tweet' or 'reddit_post'
|
||||
score = Column(Float, nullable=False, index=True) # Compound score
|
||||
sentiment_type = Column(String(20), nullable=False, index=True) # 'positive', 'negative', 'neutral'
|
||||
positive = Column(Float, nullable=False, default=0.0)
|
||||
negative = Column(Float, nullable=False, default=0.0)
|
||||
neutral = Column(Float, nullable=False, default=0.0)
|
||||
created_at = Column(DateTime, nullable=False, index=True, default=datetime.utcnow)
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_sentiment_scores_entity', 'entity_id', 'entity_type'),
|
||||
Index('idx_sentiment_scores_score', 'score'),
|
||||
Index('idx_sentiment_scores_type', 'sentiment_type'),
|
||||
Index('idx_sentiment_scores_created_at', 'created_at'),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<SentimentScore(id={self.id}, entity_id={self.entity_id}, sentiment_type={self.sentiment_type})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert sentiment score model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'entity_id': self.entity_id,
|
||||
'entity_type': self.entity_type,
|
||||
'score': self.score,
|
||||
'sentiment_type': self.sentiment_type,
|
||||
'positive': self.positive,
|
||||
'negative': self.negative,
|
||||
'neutral': self.neutral,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None
|
||||
}
|
||||
63
backend/app/models/tweet.py
Normal file
63
backend/app/models/tweet.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
SQLAlchemy model for tweets.
|
||||
|
||||
This module defines the database model for storing tweets collected
|
||||
from Twitter, Reddit, and RSS sources.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Index, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Tweet(Base):
|
||||
"""
|
||||
Model for storing tweets collected from social media sources.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
tweet_id: Unique identifier from the source platform
|
||||
text: Tweet content
|
||||
created_at: Timestamp when the tweet was created
|
||||
retweet_count: Number of retweets
|
||||
like_count: Number of likes
|
||||
match_id: Foreign key to matches table
|
||||
source: Source platform (twitter, reddit, rss)
|
||||
"""
|
||||
__tablename__ = "tweets"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
tweet_id = Column(String(255), unique=True, nullable=False, index=True)
|
||||
text = Column(String(1000), nullable=False)
|
||||
created_at = Column(DateTime, nullable=False, index=True)
|
||||
retweet_count = Column(Integer, default=0)
|
||||
like_count = Column(Integer, default=0)
|
||||
match_id = Column(Integer, ForeignKey('matches.id'), nullable=True, index=True)
|
||||
source = Column(String(50), default="twitter")
|
||||
|
||||
# Indexes for performance
|
||||
__table_args__ = (
|
||||
Index('idx_tweets_match_id_source', 'match_id', 'source'),
|
||||
Index('idx_tweets_created_at', 'created_at'),
|
||||
)
|
||||
|
||||
# Relationship with match
|
||||
match = relationship("Match", back_populates="tweets")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Tweet(id={self.id}, tweet_id={self.tweet_id}, match_id={self.match_id})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert tweet model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'tweet_id': self.tweet_id,
|
||||
'text': self.text,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
'retweet_count': self.retweet_count,
|
||||
'like_count': self.like_count,
|
||||
'match_id': self.match_id,
|
||||
'source': self.source
|
||||
}
|
||||
27
backend/app/models/user.py
Normal file
27
backend/app/models/user.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""Modèle SQLAlchemy pour les utilisateurs."""
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
"""Modèle de base de données pour les utilisateurs."""
|
||||
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True, nullable=False)
|
||||
name = Column(String, nullable=True)
|
||||
password_hash = Column(String, nullable=True)
|
||||
is_premium = Column(Boolean, default=False, nullable=False)
|
||||
referral_code = Column(String, unique=True, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
daily_predictions_count = Column(Integer, default=0)
|
||||
last_prediction_date = Column(DateTime, nullable=True)
|
||||
|
||||
# Relationships
|
||||
user_predictions = relationship("UserPrediction", back_populates="user", cascade="all, delete-orphan")
|
||||
badges = relationship("UserBadge", back_populates="user", cascade="all, delete-orphan")
|
||||
api_keys = relationship("ApiKey", back_populates="user", cascade="all, delete-orphan")
|
||||
62
backend/app/models/user_prediction.py
Normal file
62
backend/app/models/user_prediction.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
SQLAlchemy model for user_predictions.
|
||||
|
||||
This module defines the database model for tracking which predictions
|
||||
each user has viewed and their accuracy.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, DateTime, Boolean, ForeignKey, Index, UniqueConstraint
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class UserPrediction(Base):
|
||||
"""
|
||||
Model for tracking predictions viewed by users.
|
||||
|
||||
This table tracks:
|
||||
- Which predictions each user has viewed
|
||||
- When they viewed them
|
||||
- Whether the prediction was correct (when match result is known)
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
user_id: Foreign key to users table
|
||||
prediction_id: Foreign key to predictions table
|
||||
viewed_at: Timestamp when user viewed the prediction
|
||||
was_correct: True if prediction was correct, False if incorrect, NULL if match not completed
|
||||
"""
|
||||
__tablename__ = "user_predictions"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
prediction_id = Column(Integer, ForeignKey("predictions.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
viewed_at = Column(DateTime, nullable=False, index=True)
|
||||
was_correct = Column(Boolean, nullable=True, comment='True if prediction was correct, False if incorrect, NULL if match not completed')
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="user_predictions")
|
||||
prediction = relationship("Prediction", back_populates="user_predictions")
|
||||
|
||||
# Constraints and indexes
|
||||
__table_args__ = (
|
||||
Index('idx_user_predictions_user_id', 'user_id'),
|
||||
Index('idx_user_predictions_prediction_id', 'prediction_id'),
|
||||
Index('idx_user_predictions_viewed_at', 'viewed_at'),
|
||||
UniqueConstraint('user_id', 'prediction_id', name='uq_user_predictions_user_prediction'),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<UserPrediction(id={self.id}, user_id={self.user_id}, prediction_id={self.prediction_id}, viewed_at={self.viewed_at})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert user prediction model to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'user_id': self.user_id,
|
||||
'prediction_id': self.prediction_id,
|
||||
'viewed_at': self.viewed_at.isoformat() if self.viewed_at else None,
|
||||
'was_correct': self.was_correct
|
||||
}
|
||||
Reference in New Issue
Block a user