chartbastan/backend/app/services/energy_service.py
2026-02-01 09:31:38 +01:00

279 lines
7.2 KiB
Python

"""
Energy Score Service.
This module provides business logic for energy score calculation and storage.
"""
from datetime import datetime
from typing import List, Dict, Optional
from sqlalchemy.orm import Session
from sqlalchemy import and_
from app.ml.energy_calculator import (
calculate_energy_score,
adjust_weights_for_degraded_mode,
get_source_weights
)
from app.models.energy_score import EnergyScore
from app.schemas.energy_score import (
EnergyScoreCreate,
EnergyScoreUpdate,
EnergyScoreCalculationRequest,
EnergyScoreCalculationResponse
)
from app.database import get_db
def calculate_and_store_energy_score(
db: Session,
request: EnergyScoreCalculationRequest
) -> EnergyScore:
"""
Calculate energy score and store it in the database.
Args:
db: Database session
request: Energy score calculation request
Returns:
Created EnergyScore object
"""
# Calculate energy score using the ML module
result = calculate_energy_score(
match_id=request.match_id,
team_id=request.team_id,
twitter_sentiments=request.twitter_sentiments or [],
reddit_sentiments=request.reddit_sentiments or [],
rss_sentiments=request.rss_sentiments or [],
tweets_with_timestamps=request.tweets_with_timestamps or []
)
# Get adjusted weights for degraded mode tracking
available_sources = result['sources_used']
original_weights = get_source_weights()
adjusted_weights = adjust_weights_for_degraded_mode(
original_weights=original_weights,
available_sources=available_sources
)
# Create energy score record
energy_score = EnergyScore(
match_id=request.match_id,
team_id=request.team_id,
score=result['score'],
confidence=result['confidence'],
sources_used=result['sources_used'],
twitter_score=_calculate_component_score(request.twitter_sentiments),
reddit_score=_calculate_component_score(request.reddit_sentiments),
rss_score=_calculate_component_score(request.rss_sentiments),
temporal_factor=result.get('temporal_factor'),
twitter_weight=adjusted_weights.get('twitter'),
reddit_weight=adjusted_weights.get('reddit'),
rss_weight=adjusted_weights.get('rss'),
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
# Save to database
db.add(energy_score)
db.commit()
db.refresh(energy_score)
return energy_score
def _calculate_component_score(sentiments: Optional[List[Dict]]) -> Optional[float]:
"""
Calculate component score for a single source.
Args:
sentiments: List of sentiment scores
Returns:
Component score or None if no sentiments
"""
if not sentiments:
return None
# Simple average of compound scores
total = sum(s.get('compound', 0) for s in sentiments)
return total / len(sentiments) if sentiments else None
def get_energy_score(
db: Session,
energy_score_id: int
) -> Optional[EnergyScore]:
"""
Get an energy score by ID.
Args:
db: Database session
energy_score_id: ID of the energy score
Returns:
EnergyScore object or None
"""
return db.query(EnergyScore).filter(EnergyScore.id == energy_score_id).first()
def get_energy_scores_by_match(
db: Session,
match_id: int
) -> List[EnergyScore]:
"""
Get all energy scores for a specific match.
Args:
db: Database session
match_id: ID of the match
Returns:
List of EnergyScore objects
"""
return db.query(EnergyScore).filter(EnergyScore.match_id == match_id).all()
def get_energy_scores_by_team(
db: Session,
team_id: int
) -> List[EnergyScore]:
"""
Get all energy scores for a specific team.
Args:
db: Database session
team_id: ID of the team
Returns:
List of EnergyScore objects
"""
return db.query(EnergyScore).filter(EnergyScore.team_id == team_id).all()
def get_energy_score_by_match_and_team(
db: Session,
match_id: int,
team_id: int
) -> Optional[EnergyScore]:
"""
Get the most recent energy score for a specific match and team.
Args:
db: Database session
match_id: ID of the match
team_id: ID of the team
Returns:
EnergyScore object or None
"""
return (db.query(EnergyScore)
.filter(and_(EnergyScore.match_id == match_id, EnergyScore.team_id == team_id))
.order_by(EnergyScore.created_at.desc())
.first())
def update_energy_score(
db: Session,
energy_score_id: int,
update: EnergyScoreUpdate
) -> Optional[EnergyScore]:
"""
Update an existing energy score.
Args:
db: Database session
energy_score_id: ID of the energy score
update: Updated energy score data
Returns:
Updated EnergyScore object or None
"""
energy_score = get_energy_score(db, energy_score_id)
if not energy_score:
return None
# Update fields
update_data = update.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(energy_score, key, value)
energy_score.updated_at = datetime.utcnow()
# Save to database
db.commit()
db.refresh(energy_score)
return energy_score
def delete_energy_score(
db: Session,
energy_score_id: int
) -> bool:
"""
Delete an energy score.
Args:
db: Database session
energy_score_id: ID of the energy score
Returns:
True if deleted, False if not found
"""
energy_score = get_energy_score(db, energy_score_id)
if not energy_score:
return False
db.delete(energy_score)
db.commit()
return True
def list_energy_scores(
db: Session,
match_id: Optional[int] = None,
team_id: Optional[int] = None,
min_score: Optional[float] = None,
max_score: Optional[float] = None,
min_confidence: Optional[float] = None,
limit: int = 10,
offset: int = 0
) -> List[EnergyScore]:
"""
List energy scores with optional filters.
Args:
db: Database session
match_id: Optional filter by match ID
team_id: Optional filter by team ID
min_score: Optional filter by minimum score
max_score: Optional filter by maximum score
min_confidence: Optional filter by minimum confidence
limit: Maximum number of results
offset: Offset for pagination
Returns:
List of EnergyScore objects
"""
query = db.query(EnergyScore)
# Apply filters
if match_id is not None:
query = query.filter(EnergyScore.match_id == match_id)
if team_id is not None:
query = query.filter(EnergyScore.team_id == team_id)
if min_score is not None:
query = query.filter(EnergyScore.score >= min_score)
if max_score is not None:
query = query.filter(EnergyScore.score <= max_score)
if min_confidence is not None:
query = query.filter(EnergyScore.confidence >= min_confidence)
# Apply pagination and ordering
query = query.order_by(EnergyScore.created_at.desc())
query = query.offset(offset).limit(limit)
return query.all()