Spaces:
Sleeping
Sleeping
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel | |
| from typing import List | |
| from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect | |
| from models import RegisterRequest, LoginRequest, UserResponse | |
| from auth import register_user, authenticate_user | |
| from schemas import ChatRequest, ChatResponse, CoachRequest, CoachResponse | |
| from chatbot import generate_chat_reply | |
| from schemas import EventCreate, EventResponse | |
| from events_service import add_event, get_all_events, get_events_by_date | |
| from typing import List | |
| from datetime import date | |
| from schemas import FitnessRequest, FitnessResponse | |
| from services import ( | |
| calculate_bmi, | |
| calculate_bmr, | |
| diet_recommendations, | |
| fitness_recommendations, | |
| ) | |
| from coach_routes import router as coach_router | |
| from player_routes import router as player_router | |
| from stats_routes import router as stats_router | |
| from feedback_routes import router as feedback_router | |
| from cv_model import router as model | |
| from tools import get_nearby_sports_events | |
| # Pose Feedback Services | |
| from pose_feedback import PoseFeedbackService, TTSService, VoiceCommandService | |
| app = FastAPI(title="Sportans Backend") | |
| # ------------------ POSE FEEDBACK SERVICES ------------------ | |
| pose_feedback_service = PoseFeedbackService() | |
| tts_service = TTSService() | |
| voice_service = VoiceCommandService() | |
| # ------------------ MODELS ------------------ | |
| class Mentor(BaseModel): | |
| name: str | |
| imageUrl: str | |
| class TeamMember(BaseModel): | |
| name: str | |
| class AboutResponse(BaseModel): | |
| title: str | |
| teamName: str | |
| description: List[str] | |
| mentor: Mentor | |
| teamMembers: List[TeamMember] | |
| teamImageUrl: str | |
| # ------------------ STATIC FILES ------------------ | |
| app.mount("/static", StaticFiles(directory="images"), name="static") | |
| # ------------------ ROUTES ------------------ | |
| async def about_us(): | |
| return { | |
| "title": "About Us", | |
| "teamName": "Team Sportans", | |
| "description": [ | |
| "In our first year of studies, we embarked on a remarkable journey to create this project, a journey that served as a testament to our collective ideas and boundless creativity.", | |
| "We wish to extend our deepest and most sincere gratitude to our mentor, Nandhini K. Her unwavering guidance and steadfast support were invaluable.", | |
| "There were countless late nights spent debugging code and endless iterations as we sought perfection.", | |
| "As we present our project to the world, we are grateful for your time and hope it proves inspiring and informative." | |
| ], | |
| "mentor": { | |
| "name": "Anupkumar Bongale", | |
| "imageUrl": "/static/mentor.jpg" | |
| }, | |
| "teamMembers": [ | |
| {"name": "Karthikeshwar Karne Reddy"}, | |
| {"name": "Karan Chauhan"}, | |
| {"name": "Krrish Sinha"} | |
| ], | |
| "teamImageUrl": "/static/members.jpg" | |
| } | |
| async def register(data: RegisterRequest): | |
| user_id, error = register_user(data) | |
| if error: | |
| raise HTTPException(status_code=400, detail=error) | |
| return {"message": "Registration successful", "userId": user_id} | |
| async def login(data: LoginRequest): | |
| user = authenticate_user(data) | |
| if not user: | |
| raise HTTPException(status_code=401, detail="Invalid credentials") | |
| return user | |
| # -------- Sporta Bot -------- | |
| async def sporta_bot_chat(data: ChatRequest): | |
| try: | |
| reply, updated_history = generate_chat_reply( | |
| username=data.username, | |
| message=data.message, | |
| history=[h.dict() for h in data.history] | |
| ) | |
| return { | |
| "reply": reply, | |
| "history": updated_history | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| #-------- Sporta Coach -------- | |
| async def sporta_coach(data: CoachRequest): | |
| try: | |
| plan = generate_coaching_plan(data) | |
| return {"plan": plan} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| #-------Nearby Events------- | |
| def get_events(city: str): | |
| try: | |
| result = get_nearby_sports_events.run(city) | |
| return { | |
| "status": "success", | |
| "city": city, | |
| "events": result | |
| } | |
| except Exception as e: | |
| return { | |
| "status": "error", | |
| "message": str(e) | |
| } | |
| # -------- Add Event -------- | |
| async def create_event(event: EventCreate): | |
| add_event(event) | |
| return {"message": "Event added successfully"} | |
| # -------- Get All Events -------- | |
| async def fetch_events(): | |
| rows = get_all_events() | |
| return rows | |
| # -------- Search Events By Date -------- | |
| async def search_events(eventDate: date): | |
| rows = get_events_by_date(eventDate) | |
| return rows | |
| def get_fitness_data(data: FitnessRequest): | |
| bmi = calculate_bmi(data.weight, data.height) | |
| bmr = calculate_bmr(data.weight, data.height, data.age, data.gender) | |
| return FitnessResponse( | |
| bmi=bmi, | |
| bmr=bmr, | |
| fitness_recommendations=fitness_recommendations(bmi), | |
| diet_recommendations=diet_recommendations(bmr), | |
| ) | |
| app.include_router(coach_router) | |
| app.include_router(player_router) | |
| app.include_router(stats_router) | |
| app.include_router(feedback_router) | |
| app.include_router(model) | |
| # ------------------ POSE FEEDBACK WEBSOCKET ------------------ | |
| async def pose_feedback_websocket(websocket: WebSocket): | |
| """ | |
| Real-time pose feedback via WebSocket | |
| Receives pose keypoints and provides voice feedback based on: | |
| 1. User voice request "Am I doing right?" | |
| 2. Critical safety issues | |
| 3. Holding pose for 15+ seconds | |
| Anti-spam: Max 1 feedback per 10 seconds, top 2 corrections only | |
| """ | |
| await websocket.accept() | |
| try: | |
| while True: | |
| # Receive data from frontend | |
| data = await websocket.receive_json() | |
| action = data.get("action") | |
| if action == "analyze_pose": | |
| # Frontend sends pose data every frame | |
| keypoints = data.get("keypoints") | |
| pose_name = data.get("pose_name") | |
| if not keypoints or not pose_name: | |
| await websocket.send_json({ | |
| "type": "error", | |
| "message": "Missing keypoints or pose_name" | |
| }) | |
| continue | |
| # Check if feedback should be given | |
| result = pose_feedback_service.should_give_feedback( | |
| keypoints=keypoints, | |
| pose_name=pose_name, | |
| voice_trigger=False | |
| ) | |
| if result: | |
| # Generate voice audio | |
| audio_bytes = tts_service.text_to_speech(result["feedback_text"]) | |
| # Send text feedback first | |
| await websocket.send_json({ | |
| "type": "feedback", | |
| "text": result["feedback_text"], | |
| "corrections": result["corrections"], | |
| "reason": result["reason"] | |
| }) | |
| # Send audio bytes | |
| await websocket.send_bytes(audio_bytes) | |
| else: | |
| # No feedback needed | |
| await websocket.send_json({ | |
| "type": "no_feedback" | |
| }) | |
| elif action == "voice_command": | |
| # Frontend sends audio when user speaks | |
| keypoints = data.get("keypoints") | |
| pose_name = data.get("pose_name") | |
| audio_data = data.get("audio") # Base64 or bytes | |
| if not keypoints or not pose_name: | |
| await websocket.send_json({ | |
| "type": "error", | |
| "message": "Missing keypoints or pose_name" | |
| }) | |
| continue | |
| # Check if user said trigger phrase like "Am I doing right?" | |
| is_trigger = voice_service.listen_for_trigger(audio_data) | |
| if is_trigger: | |
| # User explicitly requested feedback | |
| result = pose_feedback_service.should_give_feedback( | |
| keypoints=keypoints, | |
| pose_name=pose_name, | |
| voice_trigger=True | |
| ) | |
| if result: | |
| audio_bytes = tts_service.text_to_speech(result["feedback_text"]) | |
| await websocket.send_json({ | |
| "type": "feedback", | |
| "text": result["feedback_text"], | |
| "corrections": result["corrections"], | |
| "reason": "voice_requested" | |
| }) | |
| await websocket.send_bytes(audio_bytes) | |
| else: | |
| # No corrections needed | |
| audio_bytes = tts_service.text_to_speech("Great job! Your pose looks good.") | |
| await websocket.send_json({ | |
| "type": "feedback", | |
| "text": "Great job! Your pose looks good.", | |
| "corrections": [], | |
| "reason": "voice_requested" | |
| }) | |
| await websocket.send_bytes(audio_bytes) | |
| elif action == "reset": | |
| # Reset feedback state (new session) | |
| pose_feedback_service.reset_state() | |
| await websocket.send_json({ | |
| "type": "reset_complete" | |
| }) | |
| except WebSocketDisconnect: | |
| print("Client disconnected from pose feedback") | |
| except Exception as e: | |
| print(f"WebSocket error: {e}") | |
| try: | |
| await websocket.send_json({ | |
| "type": "error", | |
| "message": str(e) | |
| }) | |
| except: | |
| pass | |