LifeRPG_v2.0/modern/backend/momentum_system.py
TLimoges33 2b961611fd
🚀 Major Enhancement: Complete AI-Powered LifeRPG Platform with Git LFS
 New Features:
- AI-powered habit creation with natural language processing
- HuggingFace transformers integration for sentiment analysis (tracked via Git LFS)
- Advanced predictive analytics and behavioral insights
- Voice & image input capabilities for hands-free habit tracking
- Real-time notifications and community features
- Plugin system with extensible architecture

🔧 Technical Improvements:
- Comprehensive FastAPI backend with 30+ endpoints
- React frontend with PWA capabilities
- Advanced authentication with 2FA support
- RBAC authorization system
- Comprehensive security features (CSRF, rate limiting, audit logging)
- Database migrations and health monitoring
- Docker containerization support
- Git LFS configured for large AI model files (2+ GB)

📚 Documentation & DevOps:
- Complete deployment guides for multiple platforms
- Professional README with feature highlights
- GitHub Actions CI/CD workflows
- Comprehensive API documentation
- Security audit roadmap and compliance framework
- Setup scripts for development environment

🧪 Testing & Quality:
- Comprehensive test suite with 20+ test modules
- Setup verification scripts
- Working development environment with both backend and frontend
- Health checks and monitoring systems

🌟 Ready for:
- Portfolio showcasing
- Community contributions
- Production deployment
- Professional presentation
2025-09-28 21:29:19 +00:00

388 lines
14 KiB
Python

"""
Enhanced Momentum System - Matching AHK's time-decay momentum mechanics
This module implements the momentum system that matches the legacy AutoHotkey
version, including daily decay and completion-based momentum boosts.
"""
from datetime import datetime, timedelta
from typing import Dict, List, Optional
from sqlalchemy.orm import Session
from sqlalchemy import and_
import models
import logging
logger = logging.getLogger(__name__)
class MomentumService:
"""Service for managing user momentum with time-based decay and boosts."""
# Constants matching AHK version
DAILY_DECAY_RATE = 15 # 15% daily decay
COMPLETION_BOOST = 5 # 5 points per completion
MAX_MOMENTUM = 100 # Maximum momentum level
MIN_MOMENTUM = 0 # Minimum momentum level
def __init__(self, db: Session):
self.db = db
def get_user_momentum(self, user_id: int) -> Dict:
"""Get current user momentum with calculation if needed."""
user = (self.db.query(models.User)
.filter(models.User.id == user_id).first())
if not user:
raise ValueError(f"User {user_id} not found")
# Get or create momentum record
momentum_record = self._get_or_create_momentum_record(user_id)
# Calculate current momentum with decay
current_momentum = self._calculate_current_momentum(momentum_record)
# Update if changed
if current_momentum != momentum_record.momentum:
momentum_record.momentum = current_momentum
momentum_record.last_updated = datetime.utcnow()
self.db.commit()
return {
'user_id': user_id,
'momentum': current_momentum,
'last_updated': momentum_record.last_updated.isoformat(),
'momentum_color': self._get_momentum_color(current_momentum),
'momentum_level': self._get_momentum_level(current_momentum),
'days_since_update': (
datetime.utcnow() - momentum_record.last_updated
).days
}
def update_momentum_for_completion(
self, user_id: int, habit_difficulty: int = 1
) -> Dict:
"""Update momentum when a habit is completed."""
momentum_record = self._get_or_create_momentum_record(user_id)
# First apply any pending decay
current_momentum = self._calculate_current_momentum(momentum_record)
# Apply completion boost (scaled by difficulty)
boost = self.COMPLETION_BOOST * habit_difficulty
new_momentum = min(self.MAX_MOMENTUM, current_momentum + boost)
# Update record
momentum_record.momentum = new_momentum
momentum_record.last_updated = datetime.utcnow()
self.db.commit()
logger.info(
f"Momentum updated for user {user_id}: "
f"{current_momentum} -> {new_momentum} (+{boost})"
)
return {
'previous_momentum': current_momentum,
'new_momentum': new_momentum,
'boost_applied': boost,
'momentum_color': self._get_momentum_color(new_momentum),
'momentum_level': self._get_momentum_level(new_momentum)
}
def apply_daily_momentum_decay(
self, user_id: Optional[int] = None
) -> List[Dict]:
"""Apply daily momentum decay. If user_id is None, applies to all."""
if user_id:
users = [
self.db.query(models.User)
.filter(models.User.id == user_id).first()
]
else:
users = self.db.query(models.User).all()
results = []
for user in users:
if not user:
continue
momentum_record = self._get_or_create_momentum_record(user.id)
old_momentum = momentum_record.momentum
new_momentum = self._calculate_current_momentum(momentum_record)
if new_momentum != old_momentum:
momentum_record.momentum = new_momentum
momentum_record.last_updated = datetime.utcnow()
results.append({
'user_id': user.id,
'old_momentum': old_momentum,
'new_momentum': new_momentum,
'decay_applied': old_momentum - new_momentum
})
self.db.commit()
return results
def get_momentum_history(self, user_id: int, days: int = 30) -> Dict:
"""Get momentum history for visualization."""
end_date = datetime.utcnow()
start_date = end_date - timedelta(days=days)
# Get habit completions for the period
completions = self.db.query(models.Log).filter(
and_(
models.Log.user_id == user_id,
models.Log.action == 'completed',
models.Log.created_at >= start_date
)
).order_by(models.Log.created_at).all()
# Simulate momentum over time
momentum_history = []
current_momentum = self._get_or_create_momentum_record(user_id).momentum
# Work backwards from current momentum
completion_dates = [c.created_at.date() for c in completions]
for i in range(days):
date = (end_date - timedelta(days=i)).date()
# Count completions on this date
completions_count = completion_dates.count(date)
# Estimate momentum for this date
if i == 0:
momentum = current_momentum
else:
# Apply reverse decay and subtract completion boosts
momentum = momentum_history[i-1]['momentum']
momentum += self.DAILY_DECAY_RATE # Add back the decay
momentum -= completions_count * self.COMPLETION_BOOST # Subtract the boost
momentum = max(self.MIN_MOMENTUM, min(self.MAX_MOMENTUM, momentum))
momentum_history.append({
'date': date.isoformat(),
'momentum': max(0, momentum),
'completions': completions_count,
'momentum_level': self._get_momentum_level(momentum)
})
# Reverse to get chronological order
momentum_history.reverse()
return {
'user_id': user_id,
'period_days': days,
'history': momentum_history,
'average_momentum': sum(h['momentum'] for h in momentum_history) / len(momentum_history),
'total_completions': sum(h['completions'] for h in momentum_history)
}
def get_momentum_insights(self, user_id: int) -> Dict:
"""Get momentum insights and recommendations."""
current_data = self.get_user_momentum(user_id)
history = self.get_momentum_history(user_id, 7) # Last week
insights = []
recommendations = []
# Analyze current momentum level
momentum = current_data['momentum']
if momentum >= 80:
insights.append("🔥 You're on fire! Your momentum is excellent.")
recommendations.append("Keep up the great work and maintain consistency.")
elif momentum >= 60:
insights.append("💪 Strong momentum! You're building good habits.")
recommendations.append("Try to complete habits daily to maintain this level.")
elif momentum >= 40:
insights.append("⚡ Moderate momentum. Room for improvement.")
recommendations.append("Focus on completing at least one habit daily.")
elif momentum >= 20:
insights.append("📈 Low momentum. Time to get back on track.")
recommendations.append("Start with easier habits to rebuild momentum.")
else:
insights.append("🎯 Fresh start! Let's build momentum together.")
recommendations.append("Begin with one simple habit and complete it daily.")
# Analyze recent trend
recent_momentum = [h['momentum'] for h in history['history'][-3:]]
if len(recent_momentum) >= 2:
trend = recent_momentum[-1] - recent_momentum[0]
if trend > 10:
insights.append("📈 Your momentum is trending upward!")
elif trend < -10:
insights.append("📉 Your momentum has been declining recently.")
# Days without decay
days_since_update = current_data['days_since_update']
if days_since_update >= 3:
recommendations.append(f"It's been {days_since_update} days since your last activity. Complete a habit to prevent further momentum decay.")
return {
'user_id': user_id,
'current_momentum': momentum,
'momentum_level': current_data['momentum_level'],
'insights': insights,
'recommendations': recommendations,
'days_since_update': days_since_update,
'weekly_average': history['average_momentum']
}
def _get_or_create_momentum_record(self, user_id: int) -> models.UserMomentum:
"""Get or create momentum record for user."""
momentum_record = self.db.query(models.UserMomentum).filter(
models.UserMomentum.user_id == user_id
).first()
if not momentum_record:
momentum_record = models.UserMomentum(
user_id=user_id,
momentum=50, # Start with moderate momentum
last_updated=datetime.utcnow()
)
self.db.add(momentum_record)
self.db.commit()
return momentum_record
def _calculate_current_momentum(self, momentum_record: models.UserMomentum) -> float:
"""Calculate current momentum with time-based decay."""
if not momentum_record.last_updated:
return momentum_record.momentum
# Calculate days since last update
now = datetime.utcnow()
days_elapsed = (now - momentum_record.last_updated).days
if days_elapsed == 0:
return momentum_record.momentum
# Apply daily decay (15% per day, matching AHK)
current_momentum = momentum_record.momentum
for _ in range(days_elapsed):
decay = current_momentum * (self.DAILY_DECAY_RATE / 100)
current_momentum = max(self.MIN_MOMENTUM, current_momentum - decay)
return round(current_momentum, 2)
def _get_momentum_color(self, momentum: float) -> str:
"""Get color code for momentum level (matching AHK HUD colors)."""
if momentum >= 70:
return 'green'
elif momentum >= 40:
return 'yellow'
else:
return 'red'
def _get_momentum_level(self, momentum: float) -> str:
"""Get descriptive level for momentum."""
if momentum >= 90:
return 'Legendary'
elif momentum >= 80:
return 'Excellent'
elif momentum >= 70:
return 'Great'
elif momentum >= 60:
return 'Good'
elif momentum >= 50:
return 'Fair'
elif momentum >= 40:
return 'Moderate'
elif momentum >= 30:
return 'Low'
elif momentum >= 20:
return 'Poor'
else:
return 'Critical'
# Add momentum model if it doesn't exist
def add_momentum_model_if_needed():
"""Helper to add momentum model to models.py if not present."""
momentum_model = '''
class UserMomentum(Base):
__tablename__ = 'user_momentum'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
momentum = Column(Float, default=50.0) # Current momentum level (0-100)
last_updated = Column(DateTime, server_default=func.current_timestamp())
user = relationship("User", back_populates="momentum")
__table_args__ = (
Index('idx_user_momentum_user_id', 'user_id'),
)
'''
return momentum_model
# FastAPI endpoints for momentum system
def get_momentum_endpoints():
"""Return FastAPI endpoints for momentum system."""
endpoints = '''
@app.get('/api/v1/momentum/{user_id}')
def get_user_momentum_endpoint(
user_id: int,
user=Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get current user momentum."""
if user.id != user_id and not user.is_admin:
raise HTTPException(403, "Access denied")
momentum_service = MomentumService(db)
return momentum_service.get_user_momentum(user_id)
@app.post('/api/v1/momentum/{user_id}/boost')
def boost_momentum_endpoint(
user_id: int,
difficulty: int = 1,
user=Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Boost momentum for habit completion."""
if user.id != user_id:
raise HTTPException(403, "Access denied")
momentum_service = MomentumService(db)
return momentum_service.update_momentum_for_completion(user_id, difficulty)
@app.get('/api/v1/momentum/{user_id}/history')
def get_momentum_history_endpoint(
user_id: int,
days: int = 30,
user=Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get momentum history for visualization."""
if user.id != user_id and not user.is_admin:
raise HTTPException(403, "Access denied")
momentum_service = MomentumService(db)
return momentum_service.get_momentum_history(user_id, days)
@app.get('/api/v1/momentum/{user_id}/insights')
def get_momentum_insights_endpoint(
user_id: int,
user=Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get momentum insights and recommendations."""
if user.id != user_id and not user.is_admin:
raise HTTPException(403, "Access denied")
momentum_service = MomentumService(db)
return momentum_service.get_momentum_insights(user_id)
@app.post('/api/v1/admin/momentum/decay')
def apply_momentum_decay_endpoint(
admin_user=Depends(require_admin),
db: Session = Depends(get_db)
):
"""Apply momentum decay to all users (admin only)."""
momentum_service = MomentumService(db)
results = momentum_service.apply_daily_momentum_decay()
return {"updated_users": len(results), "results": results}
'''
return endpoints