tc-agent / app.py
togitoon's picture
Initial
bf5f290
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pydantic import BaseModel, Field
from database.supabase_manager import SupabaseManager
from services.scheduler import DailyChallengeScheduler
from services.challenge_agent import ChallengeAgentService
from services.challenge_service import challenge_service
from config.settings import settings
from utils.logger import configure_app_logging
from utils.error_handler import StandardAPIResponse, create_http_exception
# Configure logging
logger = configure_app_logging()
# Pydantic models for API requests/responses
class UserRegistrationRequest(BaseModel):
"""Request model for user registration."""
email: str = Field(description="User's email address")
preferences: str = Field(description="User's challenge preferences in natural language")
class UserToggleRequest(BaseModel):
"""Request model for toggling user active status."""
email: str = Field(description="User's email address")
active: bool = Field(description="Whether notifications should be active")
class TestNotificationRequest(BaseModel):
"""Request model for test notifications."""
email: str = Field(description="User's email address")
preferences: str = Field(description="User's challenge preferences for testing")
app = FastAPI(title="Topcoder Challenge Steward Agent", version="1.0.0")
# Mount static files
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
def serve_index():
return FileResponse("static/index.html")
@app.post("/api/topcoder-dry-run")
def topcoder_dry_run(request: dict):
"""Dry run endpoint showing Topcoder MCP server integration with JSON response"""
try:
# Get user preferences from request
user_preferences = request.get("preferences", "")
logger.info(f"Dry run request with preferences: {user_preferences}")
# Use the centralized challenge service
result = challenge_service.get_challenges_dry_run(user_preferences)
# Check if the result indicates success
if result.get("success"):
# Return the challenges data in the format expected by frontend
challenges = result.get("data", {}).get("challenges", [])
return {"challenges": challenges}
else:
# Return error response
return {
"error": result.get("error", "Unknown error occurred"),
"raw_response": result
}
except Exception as e:
logger.error(f"Error in topcoder_dry_run: {e}")
return {"error": f"Topcoder dry run failed: {str(e)}"}
# === NEW SUPABASE-POWERED ENDPOINTS ===
# Initialize services
supabase_manager = SupabaseManager()
challenge_agent_service = ChallengeAgentService()
daily_scheduler = DailyChallengeScheduler()
@app.post("/api/register-user")
async def register_user(request: UserRegistrationRequest):
"""Register a user with email and preferences for challenge notifications"""
try:
logger.info(f"Registering user: {request.email}")
user_data = await supabase_manager.save_user_preferences(
email=request.email,
preferences=request.preferences
)
# Get the actual user data from database to ensure accurate status
actual_user = await supabase_manager.get_user_by_email(request.email)
return {
"success": True,
"message": "User registered successfully",
"user": {
"email": request.email,
"preferences": request.preferences,
"active": actual_user.get("active", True) if actual_user else True
}
}
except Exception as e:
logger.error(f"Error registering user: {e}")
raise HTTPException(status_code=500, detail=f"Failed to register user: {str(e)}")
@app.post("/api/toggle-user")
async def toggle_user_active(request: UserToggleRequest):
"""Toggle user's active status for notifications"""
try:
logger.info(f"Toggling user {request.email} to active={request.active}")
success = await supabase_manager.toggle_user_active(
email=request.email,
active=request.active
)
if success:
action = "activated" if request.active else "deactivated"
return {
"success": True,
"message": f"User notifications {action} successfully"
}
else:
raise HTTPException(status_code=404, detail="User not found")
except HTTPException:
# Re-raise HTTPExceptions (like 404) without modification
raise
except Exception as e:
logger.error(f"Error toggling user status: {e}")
raise HTTPException(status_code=500, detail=f"Failed to toggle user status: {str(e)}")
@app.post("/api/update-preferences")
async def update_user_preferences(request: UserRegistrationRequest):
"""Update existing user's preferences"""
try:
logger.info(f"Updating preferences for user: {request.email}")
# Check if user exists first
existing_user = await supabase_manager.get_user_by_email(request.email)
if not existing_user:
raise HTTPException(status_code=404, detail="User not found")
user_data = await supabase_manager.save_user_preferences(
email=request.email,
preferences=request.preferences
)
return {
"success": True,
"message": "User preferences updated successfully",
"user": {
"email": request.email,
"preferences": request.preferences,
"active": existing_user.get("active", True)
}
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error updating user preferences: {e}")
raise HTTPException(status_code=500, detail=f"Failed to update user preferences: {str(e)}")
@app.get("/api/user/{email}")
async def get_user(email: str):
"""Get user information by email"""
try:
user = await supabase_manager.get_user_by_email(email)
if user:
return {
"success": True,
"user": user
}
else:
raise HTTPException(status_code=404, detail="User not found")
except HTTPException:
# Re-raise HTTPExceptions (like 404) without modification
raise
except Exception as e:
logger.error(f"Error getting user: {e}")
raise HTTPException(status_code=500, detail=f"Failed to get user: {str(e)}")
@app.post("/api/trigger-daily-notifications")
async def trigger_daily_notifications():
"""Manually trigger daily notifications for testing"""
try:
logger.info("Manually triggering daily notifications")
await daily_scheduler.run_now_for_testing()
return {
"success": True,
"message": "Daily notifications triggered successfully"
}
except Exception as e:
logger.error(f"Error triggering daily notifications: {e}")
raise HTTPException(status_code=500, detail=f"Failed to trigger daily notifications: {str(e)}")
# Initialize scheduler on startup
@app.on_event("startup")
async def startup_event():
"""Initialize the daily challenge scheduler on app startup"""
try:
logger.info("Starting up Topcoder Challenge Steward Agent")
logger.info("Initializing daily challenge scheduler...")
# Start the scheduler
daily_scheduler.start_scheduler()
logger.info("🚀 Topcoder Challenge Steward Agent is running at http://localhost:8000/")
except Exception as e:
logger.error(f"Error during startup: {e}")
@app.on_event("shutdown")
async def shutdown_event():
"""Clean up on app shutdown"""
try:
logger.info("Shutting down Topcoder Challenge Steward Agent")
daily_scheduler.stop_scheduler()
logger.info("Application shutdown completed successfully")
except Exception as e:
logger.error(f"Error during shutdown: {e}")