help@rskworld.in +91 93305 39277
RSK World
  • Home
  • Development
    • Web Development
    • Mobile Apps
    • Software
    • Games
    • Project
  • Technologies
    • Data Science
    • AI Development
    • Cloud Development
    • Blockchain
    • Cyber Security
    • Dev Tools
    • Testing Tools
  • Blog
  • About
  • Contact

Theme Settings

Color Scheme
Display Options
Font Size
100%
Back to Project
RSK World
fitness-coach-bot
/
static
/
js
RSK World
fitness-coach-bot
Fitness Coach Bot - Python + Flask + SQLAlchemy + Workout Plans + Exercise Guidance + Health Tracking + AI Fitness Coach
js
  • analytics_dashboard.js11.7 KB
  • app.js11.7 KB
  • pose_detection.js21.3 KB
  • voice_recognition.js8 KB
__init__.pyfitness_coach.cpython-313.pycapp.pyHOW_TO_CREATE_RELEASE.mdfitness_coach.dbpose_detection.jsapp.js
models/__init__.py
Raw Download
Find: Go to:
"""
Models Package for Fitness Coach Bot
Author: RSK World (https://rskworld.in)
Founded by: Molla Samser
Designer & Tester: Rima Khatun
Contact: help@rskworld.in, +91 93305 39277
Year: 2026
"""

from .fitness_models import db, User, WorkoutPlan, Exercise, Progress, HealthTip, WorkoutExercise

__all__ = ['db', 'User', 'WorkoutPlan', 'Exercise', 'Progress', 'HealthTip', 'WorkoutExercise']
13 lines•403 B
python
fitness_coach.cpython-313.pyc

This file cannot be displayed in the browser.

Download File
app.py
Raw Download
Find: Go to:
"""
Fitness Coach Bot - A comprehensive fitness coaching chatbot
Author: RSK World (https://rskworld.in)
Founded by: Molla Samser
Designer & Tester: Rima Khatun
Contact: help@rskworld.in, +91 93305 39277
Year: 2026
Description: Fitness chatbot for workout plans, exercise guidance, and health tracking
"""

from flask import Flask, render_template, request, jsonify, session
from datetime import datetime
import json
import os
import asyncio
from models.fitness_models import db, User, WorkoutPlan, Exercise, Progress, HealthTip
from utils.fitness_coach import FitnessCoach
from utils.ai_workout_generator import AIWorkoutGenerator
from utils.nutrition_ai import NutritionAI
from utils.social_features import SocialFeatures
from utils.analytics_engine import AdvancedAnalytics
from utils.gamification_system import GamificationSystem
from utils.wearable_integration import WearableIntegration, DeviceType
from utils.voice_coach import VoiceCoach
from utils.workout_buddy_matcher import WorkoutBuddyMatcher
from utils.smart_recovery import SmartRecovery

app = Flask(__name__)
app.config['SECRET_KEY'] = 'fitness-coach-bot-2026-rskworld'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///fitness_coach.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Initialize db with app
db.init_app(app)
fitness_coach = FitnessCoach()
ai_workout_generator = AIWorkoutGenerator()
nutrition_ai = NutritionAI()
social_features = SocialFeatures()
analytics_engine = AdvancedAnalytics()
gamification_system = GamificationSystem()
wearable_integration = WearableIntegration()
voice_coach = VoiceCoach()
buddy_matcher = WorkoutBuddyMatcher()
smart_recovery = SmartRecovery()

@app.route('/')
def index():
    """Main chatbot interface"""
    return render_template('index.html')

@app.route('/api/chat', methods=['POST'])
def chat():
    """Handle chat messages and provide fitness coaching responses"""
    try:
        data = request.get_json()
        user_message = data.get('message', '')
        user_id = session.get('user_id', 'anonymous')
        
        # Get AI response
        response = fitness_coach.get_response(user_message, user_id)
        
        return jsonify({
            'success': True,
            'response': response,
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/workout-plans', methods=['GET'])
def get_workout_plans():
    """Get available workout plans"""
    try:
        plans = WorkoutPlan.query.all()
        return jsonify({
            'success': True,
            'plans': [plan.to_dict() for plan in plans]
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/exercises', methods=['GET'])
def get_exercises():
    """Get exercises by category or muscle group"""
    try:
        category = request.args.get('category')
        muscle_group = request.args.get('muscle_group')
        
        query = Exercise.query
        if category:
            query = query.filter_by(category=category)
        if muscle_group:
            query = query.filter_by(muscle_group=muscle_group)
            
        exercises = query.all()
        return jsonify({
            'success': True,
            'exercises': [exercise.to_dict() for exercise in exercises]
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/progress', methods=['POST'])
def save_progress():
    """Save user workout progress"""
    try:
        data = request.get_json()
        user_id = session.get('user_id', 'anonymous')
        
        progress = Progress(
            user_id=user_id,
            exercise_id=data.get('exercise_id'),
            sets_completed=data.get('sets_completed'),
            reps_completed=data.get('reps_completed'),
            weight_used=data.get('weight', data.get('weight_used')),
            notes=data.get('notes', ''),
            date=datetime.now()
        )
        
        db.session.add(progress)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'Progress saved successfully'
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/health-tips', methods=['GET'])
def get_health_tips():
    """Get health and fitness tips"""
    try:
        tips = HealthTip.query.order_by(db.func.random()).limit(5).all()
        return jsonify({
            'success': True,
            'tips': [tip.to_dict() for tip in tips]
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/user/profile', methods=['GET', 'POST'])
def user_profile():
    """Get or update user profile"""
    if request.method == 'GET':
        try:
            user_id = session.get('user_id')
            if user_id:
                user = User.query.get(user_id)
                if user:
                    return jsonify({
                        'success': True,
                        'user': user.to_dict()
                    })
            return jsonify({
                'success': False,
                'message': 'User not found'
            })
        except Exception as e:
            return jsonify({
                'success': False,
                'error': str(e)
            }), 500
    
    elif request.method == 'POST':
        try:
            data = request.get_json()
            user_id = session.get('user_id', 'anonymous')
            
            user = User.query.get(user_id)
            if not user:
                user = User(id=user_id)
                db.session.add(user)
            
            user.name = data.get('name', user.name)
            user.age = data.get('age', user.age)
            user.weight = data.get('weight', user.weight)
            user.height = data.get('height', user.height)
            user.fitness_goal = data.get('fitness_goal', user.fitness_goal)
            user.activity_level = data.get('activity_level', user.activity_level)
            
            db.session.commit()
            
            return jsonify({
                'success': True,
                'message': 'Profile updated successfully',
                'user': user.to_dict()
            })
        except Exception as e:
            return jsonify({
                'success': False,
                'error': str(e)
            }), 500

# Advanced Features API Endpoints

@app.route('/api/ai-workout', methods=['POST'])
def generate_ai_workout():
    """Generate AI-powered personalized workout"""
    try:
        data = request.get_json()
        user_profile = data.get('user_profile', {})
        preferences = data.get('preferences', {})
        
        workout = ai_workout_generator.generate_personalized_workout(user_profile, preferences)
        
        return jsonify({
            'success': True,
            'workout': workout
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/nutrition-analyze', methods=['POST'])
def analyze_nutrition():
    """Analyze nutrition from food image"""
    try:
        data = request.get_json()
        image_data = data.get('image_data', '')
        user_profile = data.get('user_profile', {})
        
        # Run async function in sync context
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            analysis = loop.run_until_complete(nutrition_ai.analyze_food_image(image_data, user_profile))
        finally:
            loop.close()
        
        return jsonify({
            'success': True,
            'analysis': analysis
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/social-challenge', methods=['POST'])
def create_challenge():
    """Create social fitness challenge"""
    try:
        data = request.get_json()
        challenge_data = data.get('challenge', {})
        
        challenge = social_features.create_challenge(challenge_data)
        
        return jsonify({
            'success': True,
            'challenge': challenge
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/analytics', methods=['GET'])
def get_analytics():
    """Get comprehensive analytics dashboard"""
    try:
        user_id = session.get('user_id', 'anonymous')
        timeframe = request.args.get('timeframe', 30)
        
        analytics = analytics_engine.calculate_comprehensive_analytics(user_id, timeframe)
        
        return jsonify({
            'success': True,
            'analytics': analytics
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/gamification/profile', methods=['GET'])
def get_gamification_profile():
    """Get user gamification profile"""
    try:
        user_id = session.get('user_id', 'anonymous')
        profile = gamification_system.get_user_stats_summary(user_id)
        
        return jsonify({
            'success': True,
            'profile': profile
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/wearable/connect', methods=['POST'])
def connect_wearable():
    """Connect wearable device"""
    try:
        data = request.get_json()
        device_type_str = data.get('device_type')
        user_id = session.get('user_id', 'anonymous')
        auth_data = data.get('auth_data', {})
        
        # Convert string to DeviceType enum
        try:
            device_type = DeviceType(device_type_str)
        except (ValueError, TypeError):
            return jsonify({
                'success': False,
                'error': f'Invalid device type: {device_type_str}'
            }), 400
        
        # Run async function in sync context
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            result = loop.run_until_complete(wearable_integration.connect_device(device_type, user_id, auth_data))
        finally:
            loop.close()
        
        return jsonify(result)
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/wearable/sync/<device_id>', methods=['POST'])
def sync_wearable(device_id):
    """Sync data from wearable device"""
    try:
        # Run async function in sync context
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            result = loop.run_until_complete(wearable_integration.sync_device_data(device_id))
        finally:
            loop.close()
        
        return jsonify(result)
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

# Advanced Features API Endpoints

@app.route('/api/voice/command', methods=['POST'])
def voice_command():
    """Process voice command"""
    try:
        data = request.get_json()
        transcript = data.get('transcript', '')
        
        result = voice_coach.process_voice_command(transcript)
        
        return jsonify(result)
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/voice/workout-guidance', methods=['POST'])
def workout_guidance():
    """Get voice guidance for workout"""
    try:
        data = request.get_json()
        exercise_name = data.get('exercise_name')
        set_number = data.get('set_number', 1)
        
        guidance = voice_coach.generate_workout_guidance(exercise_name, set_number)
        
        return jsonify(guidance)
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/buddy/profile', methods=['GET', 'POST'])
def buddy_profile():
    """Get or create workout buddy profile"""
    try:
        if request.method == 'POST':
            data = request.get_json()
            data['user_id'] = session.get('user_id', 'anonymous')
            result = buddy_matcher.create_buddy_profile(data)
            return jsonify(result)
        else:
            user_id = session.get('user_id', 'anonymous')
            matches = buddy_matcher.find_matches(user_id)
            return jsonify({
                'success': True,
                'matches': matches
            })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/buddy/find-matches', methods=['GET'])
def find_buddies():
    """Find workout buddy matches"""
    try:
        user_id = session.get('user_id', 'anonymous')
        limit = request.args.get('limit', 5, type=int)
        
        matches = buddy_matcher.find_matches(user_id, limit)
        
        return jsonify({
            'success': True,
            'matches': matches
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/buddy/request', methods=['POST'])
def send_buddy_request():
    """Send workout buddy request"""
    try:
        data = request.get_json()
        from_user_id = session.get('user_id', 'anonymous')
        to_user_id = data.get('to_user_id')
        message = data.get('message')
        
        result = buddy_matcher.create_buddy_request(from_user_id, to_user_id, message)
        
        return jsonify(result)
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/recovery/calculate', methods=['POST'])
def calculate_recovery():
    """Calculate recovery score and recommendations"""
    try:
        data = request.get_json()
        
        result = smart_recovery.calculate_recovery_score(data)
        
        return jsonify({
            'success': True,
            'recovery': result
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/recovery/activities', methods=['GET'])
def recovery_activities():
    """Get suggested recovery activities"""
    try:
        status = request.args.get('status', 'good')
        
        activities = smart_recovery.suggest_recovery_activities(status)
        
        return jsonify({
            'success': True,
            'activities': activities
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/pose-workout', methods=['POST'])
def save_pose_workout():
    """Save pose detection workout data"""
    try:
        data = request.get_json()
        user_id = session.get('user_id', 'anonymous')
        
        # Save to database
        progress = Progress(
            user_id=user_id,
            exercise_id=data.get('exercise_id', 1),
            sets_completed=1,
            reps_completed=data.get('reps', 0),
            weight_used=0,
            duration_seconds=data.get('duration', 0) // 1000,
            calories_burned=data.get('calories_burned', 0),
            notes=f"Pose detection workout - Form Score: {data.get('avgFormScore', 0)}%",
            date=datetime.now()
        )
        
        db.session.add(progress)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': 'Workout saved successfully'
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True, host='0.0.0.0', port=5000)
532 lines•16.5 KB
python
HOW_TO_CREATE_RELEASE.md
Raw Download

HOW_TO_CREATE_RELEASE.md

# How to Create GitHub Release v1.0.0

## ✅ Completed Steps

1. ✅ **Git Repository Initialized**
2. ✅ **All Files Committed** (30 files, 9519+ lines)
3. ✅ **Pushed to GitHub** (https://github.com/rskworld/fitness-coach-bot.git)
4. ✅ **Tag Created** (v1.0.0)
5. ✅ **Tag Pushed** to GitHub
6. ✅ **Release Notes Created** (RELEASE_NOTES_v1.0.0.md)

## 📝 Create GitHub Release (Manual Steps)

Since GitHub CLI is not installed, follow these steps to create a release on GitHub:

### Step 1: Go to GitHub Repository
1. Open your browser and navigate to:
```
https://github.com/rskworld/fitness-coach-bot
```

### Step 2: Navigate to Releases
1. Click on the **"Releases"** link on the right sidebar (or go to: https://github.com/rskworld/fitness-coach-bot/releases)
2. Click **"Create a new release"** button

### Step 3: Fill Release Details
1. **Choose a tag**: Select `v1.0.0` from the dropdown (it should already exist)
2. **Release title**: `Version 1.0.0 - Initial Release`
3. **Description**: Copy and paste the content from `RELEASE_NOTES_v1.0.0.md` file, or use this:

```
# Version 1.0.0 - Initial Release

## 🎉 Initial Release

This is the first official release of the Fitness Coach Bot - an AI-powered comprehensive fitness coaching chatbot application.

## ✨ Features

### Core Features
- **AI-Powered Chatbot**: Intelligent fitness coaching chatbot
- **Personalized Workout Generation**: AI-driven workout plans
- **Nutrition Analysis**: Food image analysis and meal planning
- **Progress Tracking**: Comprehensive workout tracking
- **User Profiles**: Detailed profile management

### Advanced Features
- **Social Features**: Fitness challenges, teams, progress sharing
- **Gamification System**: Achievements, levels, badges, leaderboards
- **Advanced Analytics**: Detailed fitness analytics with trends
- **Wearable Integration**: Support for Fitbit, Apple Watch, Garmin
- **Voice Coaching**: Voice command processing
- **Pose Detection**: Real-time exercise form analysis
- **Smart Recovery**: Recovery score calculation
- **Workout Buddy Matching**: Find compatible workout partners

## 🛠️ Technical Stack

- Flask 2.3.3, Python 3.13
- SQLAlchemy 2.0.45, SQLite
- Bootstrap 5, Vanilla JavaScript, Chart.js
- 20+ REST API endpoints

## 📦 Installation

1. Clone the repository:
```bash
git clone https://github.com/rskworld/fitness-coach-bot.git
cd fitness-coach-bot
```

2. Install dependencies:
```bash
pip install -r requirements.txt
```

3. Initialize database:
```bash
python init_db.py
```

4. Run the application:
```bash
python app.py
```

## 🐛 Fixed Issues

- ✅ Fixed SQLAlchemy compatibility with Python 3.13
- ✅ Updated all dependencies
- ✅ Fixed datetime timezone issues
- ✅ Verified all imports and syntax

## 👥 Credits

**Author:** RSK World (https://rskworld.in)
**Founder:** Molla Samser
**Designer & Tester:** Rima Khatun
**Contact:** help@rskworld.in, +91 93305 39277
**Year:** 2026
```

4. **Set as latest release**: Check the box if available
5. **Set as pre-release**: Leave unchecked (this is a stable release)

### Step 4: Publish Release
1. Click the **"Publish release"** button
2. Your release will be created and visible on the releases page

---

## 🔄 Alternative: Use GitHub CLI (If Installed)

If you install GitHub CLI in the future, you can create releases using:

```bash
gh release create v1.0.0 \
--title "Version 1.0.0 - Initial Release" \
--notes-file RELEASE_NOTES_v1.0.0.md
```

---

## 📋 Summary

✅ **Repository**: https://github.com/rskworld/fitness-coach-bot
✅ **Branch**: main
✅ **Tag**: v1.0.0 (already pushed)
✅ **Release Notes**: RELEASE_NOTES_v1.0.0.md (in repository)
⏳ **GitHub Release**: Need to create manually via web interface (see steps above)

---

## 🎯 Next Steps After Creating Release

1. Verify the release is visible on GitHub
2. Share the release link with your team
3. Update project documentation if needed
4. Consider setting up automated releases for future versions
fitness_coach.db

This file cannot be displayed in the browser.

Download File
static/js/pose_detection.js
Raw Download
Find: Go to:
/**
 * Real-time Pose Detection for Fitness Coach Bot
 * Author: RSK World (https://rskworld.in)
 * Founded by: Molla Samser
 * Designer & Tester: Rima Khatun
 * Contact: help@rskworld.in, +91 93305 39277
 * Year: 2026
 */

class PoseDetector {
    constructor() {
        this.model = null;
        this.video = null;
        this.canvas = null;
        this.ctx = null;
        this.isDetecting = false;
        this.currentExercise = null;
        this.repCount = 0;
        this.formScore = 0;
        this.feedback = [];
        this.exerciseStates = {};
        
        // Exercise-specific pose configurations
        this.exerciseConfigs = {
            'squats': {
                keyPoints: ['left_hip', 'right_hip', 'left_knee', 'right_knee', 'left_ankle', 'right_ankle'],
                idealAngles: {
                    'down': 90,
                    'up': 170
                },
                tolerance: 15,
                feedbackMessages: {
                    'good': 'Great form! Keep it up!',
                    'knees_forward': 'Keep your knees behind your toes',
                    'depth': 'Go deeper - aim for parallel',
                    'back_straight': 'Keep your back straight'
                }
            },
            'pushups': {
                keyPoints: ['left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow', 'left_wrist', 'right_wrist'],
                idealAngles: {
                    'down': 90,
                    'up': 170
                },
                tolerance: 15,
                feedbackMessages: {
                    'good': 'Perfect push-up form!',
                    'depth': 'Lower your chest more',
                    'full_extension': 'Extend arms fully at the top',
                    'core_tight': 'Keep your core engaged'
                }
            },
            'plank': {
                keyPoints: ['left_shoulder', 'right_shoulder', 'left_hip', 'right_hip', 'left_ankle', 'right_ankle'],
                idealAngles: {
                    'hold': 180
                },
                tolerance: 10,
                feedbackMessages: {
                    'good': 'Excellent plank position!',
                    'hips_high': 'Lower your hips',
                    'hips_low': 'Raise your hips',
                    'back_straight': 'Maintain straight line from head to heels'
                }
            }
        };
        
        this.init();
    }
    
    async init() {
        try {
            // Load TensorFlow.js and PoseNet models
            await this.loadModels();
            this.setupCamera();
            this.setupCanvas();
            console.log('Pose detection initialized successfully');
        } catch (error) {
            console.error('Error initializing pose detection:', error);
            this.showFallbackMessage();
        }
    }
    
    async loadModels() {
        // Load TensorFlow.js
        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js';
        document.head.appendChild(script);
        
        await new Promise(resolve => script.onload = resolve);
        
        // Load PoseNet
        const poseScript = document.createElement('script');
        poseScript.src = 'https://cdn.jsdelivr.net/npm/@tensorflow-models/posenet@2.2.2/dist/posenet.min.js';
        document.head.appendChild(poseScript);
        
        await new Promise(resolve => poseScript.onload = resolve);
        
        // Load the model
        this.model = await posenet.load({
            architecture: 'MobileNetV1',
            outputStride: 16,
            inputResolution: { width: 640, height: 480 },
            multiplier: 0.75
        });
    }
    
    setupCamera() {
        this.video = document.createElement('video');
        this.video.width = 640;
        this.video.height = 480;
        
        // Get user media
        navigator.mediaDevices.getUserMedia({
            video: { 
                width: 640, 
                height: 480,
                facingMode: 'user'
            }
        }).then(stream => {
            this.video.srcObject = stream;
            this.video.play();
        }).catch(error => {
            console.error('Error accessing camera:', error);
            this.showCameraError();
        });
    }
    
    setupCanvas() {
        this.canvas = document.getElementById('poseCanvas');
        if (!this.canvas) {
            this.canvas = document.createElement('canvas');
            this.canvas.id = 'poseCanvas';
            this.canvas.width = 640;
            this.canvas.height = 480;
            document.getElementById('poseDetectionContainer').appendChild(this.canvas);
        }
        
        this.ctx = this.canvas.getContext('2d');
    }
    
    async startDetection(exercise) {
        if (!this.model) {
            console.error('Model not loaded yet');
            return;
        }
        
        this.currentExercise = exercise;
        this.isDetecting = true;
        this.repCount = 0;
        this.formScore = 0;
        this.exerciseStates = {
            lastPosition: 'up',
            inPosition: false,
            repStartTime: null
        };
        
        this.updateUI();
        this.detectLoop();
    }
    
    stopDetection() {
        this.isDetecting = false;
        this.saveWorkoutData();
        this.showResults();
    }
    
    async detectLoop() {
        if (!this.isDetecting) return;
        
        try {
            const pose = await this.model.estimateSinglePose(this.video, {
                flipHorizontal: true
            });
            
            this.drawPose(pose);
            this.analyzePose(pose);
            
        } catch (error) {
            console.error('Error in pose detection:', error);
        }
        
        requestAnimationFrame(() => this.detectLoop());
    }
    
    drawPose(pose) {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        // Draw video
        this.ctx.save();
        this.ctx.scale(-1, 1);
        this.ctx.translate(-this.canvas.width, 0);
        this.ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
        this.ctx.restore();
        
        // Draw keypoints
        pose.keypoints.forEach(keypoint => {
            if (keypoint.score > 0.5) {
                this.ctx.beginPath();
                this.ctx.arc(keypoint.position.x, keypoint.position.y, 5, 0, 2 * Math.PI);
                this.ctx.fillStyle = '#00FF00';
                this.ctx.fill();
            }
        });
        
        // Draw skeleton
        const adjacentKeyPoints = posenet.getAdjacentKeyPoints(pose.keypoints, 0.5);
        adjacentKeyPoints.forEach(keypoints => {
            this.drawSegment(keypoints[0].position, keypoints[1].position);
        });
        
        // Draw exercise-specific indicators
        this.drawExerciseIndicators(pose);
    }
    
    drawSegment(p1, p2) {
        this.ctx.beginPath();
        this.ctx.moveTo(p1.x, p1.y);
        this.ctx.lineTo(p2.x, p2.y);
        this.ctx.lineWidth = 2;
        this.ctx.strokeStyle = '#00FF00';
        this.ctx.stroke();
    }
    
    drawExerciseIndicators(pose) {
        if (!this.currentExercise) return;
        
        const config = this.exerciseConfigs[this.currentExercise];
        if (!config) return;
        
        // Draw angle indicators for key joints
        if (this.currentExercise === 'squats') {
            this.drawKneeAngles(pose);
        } else if (this.currentExercise === 'pushups') {
            this.drawElbowAngles(pose);
        } else if (this.currentExercise === 'plank') {
            this.drawBodyLine(pose);
        }
    }
    
    drawKneeAngles(pose) {
        const leftKneeAngle = this.calculateAngle(
            this.getKeypoint(pose, 'left_hip'),
            this.getKeypoint(pose, 'left_knee'),
            this.getKeypoint(pose, 'left_ankle')
        );
        
        const rightKneeAngle = this.calculateAngle(
            this.getKeypoint(pose, 'right_hip'),
            this.getKeypoint(pose, 'right_knee'),
            this.getKeypoint(pose, 'right_ankle')
        );
        
        // Draw angle text
        this.ctx.fillStyle = '#FFD700';
        this.ctx.font = '16px Arial';
        this.ctx.fillText(`L: ${Math.round(leftKneeAngle)}°`, 50, 50);
        this.ctx.fillText(`R: ${Math.round(rightKneeAngle)}°`, 50, 80);
    }
    
    drawElbowAngles(pose) {
        const leftElbowAngle = this.calculateAngle(
            this.getKeypoint(pose, 'left_shoulder'),
            this.getKeypoint(pose, 'left_elbow'),
            this.getKeypoint(pose, 'left_wrist')
        );
        
        const rightElbowAngle = this.calculateAngle(
            this.getKeypoint(pose, 'right_shoulder'),
            this.getKeypoint(pose, 'right_elbow'),
            this.getKeypoint(pose, 'right_wrist')
        );
        
        // Draw angle text
        this.ctx.fillStyle = '#FFD700';
        this.ctx.font = '16px Arial';
        this.ctx.fillText(`L: ${Math.round(leftElbowAngle)}°`, 50, 50);
        this.ctx.fillText(`R: ${Math.round(rightElbowAngle)}°`, 50, 80);
    }
    
    drawBodyLine(pose) {
        const shoulder = this.getKeypoint(pose, 'left_shoulder');
        const hip = this.getKeypoint(pose, 'left_hip');
        const ankle = this.getKeypoint(pose, 'left_ankle');
        
        if (shoulder && hip && ankle) {
            this.ctx.beginPath();
            this.ctx.moveTo(shoulder.position.x, shoulder.position.y);
            this.ctx.lineTo(hip.position.x, hip.position.y);
            this.ctx.lineTo(ankle.position.x, ankle.position.y);
            this.ctx.strokeStyle = '#FF6B6B';
            this.ctx.lineWidth = 3;
            this.ctx.stroke();
        }
    }
    
    analyzePose(pose) {
        if (!this.currentExercise) return;
        
        const config = this.exerciseConfigs[this.currentExercise];
        if (!config) return;
        
        // Calculate exercise-specific metrics
        let metrics = {};
        
        if (this.currentExercise === 'squats') {
            metrics = this.analyzeSquat(pose);
        } else if (this.currentExercise === 'pushups') {
            metrics = this.analyzePushup(pose);
        } else if (this.currentExercise === 'plank') {
            metrics = this.analyzePlank(pose);
        }
        
        // Update rep count and form score
        this.updateRepCount(metrics);
        this.updateFormScore(metrics);
        this.provideFeedback(metrics);
    }
    
    analyzeSquat(pose) {
        const leftKneeAngle = this.calculateAngle(
            this.getKeypoint(pose, 'left_hip'),
            this.getKeypoint(pose, 'left_knee'),
            this.getKeypoint(pose, 'left_ankle')
        );
        
        const rightKneeAngle = this.calculateAngle(
            this.getKeypoint(pose, 'right_hip'),
            this.getKeypoint(pose, 'right_knee'),
            this.getKeypoint(pose, 'right_ankle')
        );
        
        const avgKneeAngle = (leftKneeAngle + rightKneeAngle) / 2;
        
        // Determine position
        let position = 'up';
        if (avgKneeAngle < 100) {
            position = 'down';
        } else if (avgKneeAngle < 150) {
            position = 'middle';
        }
        
        // Check form
        const formIssues = [];
        if (avgKneeAngle > 110 && position === 'down') {
            formIssues.push('depth');
        }
        
        return {
            position,
            avgKneeAngle,
            leftKneeAngle,
            rightKneeAngle,
            formIssues,
            isGoodForm: formIssues.length === 0 && Math.abs(avgKneeAngle - 90) < 20
        };
    }
    
    analyzePushup(pose) {
        const leftElbowAngle = this.calculateAngle(
            this.getKeypoint(pose, 'left_shoulder'),
            this.getKeypoint(pose, 'left_elbow'),
            this.getKeypoint(pose, 'left_wrist')
        );
        
        const rightElbowAngle = this.calculateAngle(
            this.getKeypoint(pose, 'right_shoulder'),
            this.getKeypoint(pose, 'right_elbow'),
            this.getKeypoint(pose, 'right_wrist')
        );
        
        const avgElbowAngle = (leftElbowAngle + rightElbowAngle) / 2;
        
        // Determine position
        let position = 'up';
        if (avgElbowAngle < 100) {
            position = 'down';
        } else if (avgElbowAngle < 150) {
            position = 'middle';
        }
        
        // Check form
        const formIssues = [];
        if (avgElbowAngle > 110 && position === 'down') {
            formIssues.push('depth');
        }
        
        return {
            position,
            avgElbowAngle,
            leftElbowAngle,
            rightElbowAngle,
            formIssues,
            isGoodForm: formIssues.length === 0 && Math.abs(avgElbowAngle - 90) < 20
        };
    }
    
    analyzePlank(pose) {
        const shoulder = this.getKeypoint(pose, 'left_shoulder');
        const hip = this.getKeypoint(pose, 'left_hip');
        const ankle = this.getKeypoint(pose, 'left_ankle');
        
        if (!shoulder || !hip || !ankle) {
            return { isGoodForm: false, formIssues: ['no_pose'] };
        }
        
        // Calculate body line angle
        const bodyAngle = this.calculateAngle(shoulder, hip, ankle);
        
        // Check form
        const formIssues = [];
        if (bodyAngle < 170) {
            formIssues.push('hips_low');
        } else if (bodyAngle > 190) {
            formIssues.push('hips_high');
        }
        
        return {
            bodyAngle,
            formIssues,
            isGoodForm: formIssues.length === 0
        };
    }
    
    updateRepCount(metrics) {
        const currentState = this.exerciseStates;
        const currentPosition = metrics.position;
        
        if (currentState.lastPosition === 'up' && currentPosition === 'down') {
            currentState.inPosition = true;
            currentState.repStartTime = Date.now();
        } else if (currentState.lastPosition === 'down' && currentPosition === 'up' && currentState.inPosition) {
            this.repCount++;
            currentState.inPosition = false;
            this.onRepComplete();
        }
        
        currentState.lastPosition = currentPosition;
    }
    
    updateFormScore(metrics) {
        if (metrics.isGoodForm) {
            this.formScore = Math.min(100, this.formScore + 1);
        } else {
            this.formScore = Math.max(0, this.formScore - 2);
        }
    }
    
    provideFeedback(metrics) {
        const config = this.exerciseConfigs[this.currentExercise];
        if (!config) return;
        
        let feedback = '';
        
        if (metrics.isGoodForm) {
            feedback = config.feedbackMessages.good;
        } else {
            // Provide specific feedback based on form issues
            for (const issue of metrics.formIssues) {
                if (config.feedbackMessages[issue]) {
                    feedback = config.feedbackMessages[issue];
                    break;
                }
            }
        }
        
        if (feedback && feedback !== this.lastFeedback) {
            this.showFeedback(feedback);
            this.lastFeedback = feedback;
        }
    }
    
    calculateAngle(pointA, pointB, pointC) {
        if (!pointA || !pointB || !pointC) return 0;
        
        const radians = Math.atan2(pointC.position.y - pointB.position.y, 
                                   pointC.position.x - pointB.position.x) -
                         Math.atan2(pointA.position.y - pointB.position.y, 
                                   pointA.position.x - pointB.position.x);
        let angle = Math.abs(radians * 180.0 / Math.PI);
        
        if (angle > 180.0) {
            angle = 360 - angle;
        }
        
        return angle;
    }
    
    getKeypoint(pose, partName) {
        return pose.keypoints.find(kp => kp.part === partName);
    }
    
    updateUI() {
        // Update rep counter
        const repElement = document.getElementById('repCounter');
        if (repElement) {
            repElement.textContent = this.repCount;
        }
        
        // Update form score
        const formElement = document.getElementById('formScore');
        if (formElement) {
            formElement.textContent = `${Math.round(this.formScore)}%`;
        }
        
        // Update exercise name
        const exerciseElement = document.getElementById('currentExercise');
        if (exerciseElement) {
            exerciseElement.textContent = this.currentExercise || 'None';
        }
    }
    
    showFeedback(message) {
        const feedbackElement = document.getElementById('poseFeedback');
        if (feedbackElement) {
            feedbackElement.textContent = message;
            feedbackElement.className = 'alert alert-info';
            
            // Auto-hide after 3 seconds
            setTimeout(() => {
                feedbackElement.textContent = '';
                feedbackElement.className = '';
            }, 3000);
        }
    }
    
    onRepComplete() {
        // Haptic feedback if available
        if (navigator.vibrate) {
            navigator.vibrate(200);
        }
        
        // Visual feedback
        const repElement = document.getElementById('repCounter');
        if (repElement) {
            repElement.classList.add('rep-complete');
            setTimeout(() => {
                repElement.classList.remove('rep-complete');
            }, 500);
        }
    }
    
    saveWorkoutData() {
        const workoutData = {
            exercise: this.currentExercise,
            reps: this.repCount,
            avgFormScore: this.formScore,
            duration: Date.now() - (this.exerciseStates.startTime || Date.now()),
            timestamp: new Date().toISOString()
        };
        
        // Save to localStorage
        let workouts = JSON.parse(localStorage.getItem('poseWorkouts') || '[]');
        workouts.push(workoutData);
        localStorage.setItem('poseWorkouts', JSON.stringify(workouts));
        
        // Send to server
        fetch('/api/pose-workout', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(workoutData)
        }).catch(error => {
            console.error('Error saving workout data:', error);
        });
    }
    
    showResults() {
        const resultsModal = document.getElementById('workoutResults');
        if (resultsModal) {
            document.getElementById('finalReps').textContent = this.repCount;
            document.getElementById('finalFormScore').textContent = `${Math.round(this.formScore)}%`;
            
            // Show achievement badges
            this.showAchievements();
            
            resultsModal.style.display = 'block';
        }
    }
    
    showAchievements() {
        const achievements = [];
        
        if (this.repCount >= 50) {
            achievements.push({ name: 'Rep Master', icon: '🏆' });
        } else if (this.repCount >= 25) {
            achievements.push({ name: 'Rep Champion', icon: '🥇' });
        } else if (this.repCount >= 10) {
            achievements.push({ name: 'Rep Hero', icon: '🥈' });
        }
        
        if (this.formScore >= 90) {
            achievements.push({ name: 'Form Perfectionist', icon: '⭐' });
        }
        
        const achievementsElement = document.getElementById('achievements');
        if (achievementsElement) {
            achievementsElement.innerHTML = achievements.map(a => 
                `<div class="achievement">
                    <span class="achievement-icon">${a.icon}</span>
                    <span class="achievement-name">${a.name}</span>
                </div>`
            ).join('');
        }
    }
    
    showFallbackMessage() {
        const container = document.getElementById('poseDetectionContainer');
        if (container) {
            container.innerHTML = `
                <div class="alert alert-warning">
                    <h4>Camera Access Required</h4>
                    <p>For pose detection, please allow camera access and use a device with a camera.</p>
                    <p>You can still use all other features of the fitness coach bot!</p>
                </div>
            `;
        }
    }
    
    showCameraError() {
        const container = document.getElementById('poseDetectionContainer');
        if (container) {
            container.innerHTML = `
                <div class="alert alert-danger">
                    <h4>Camera Error</h4>
                    <p>Unable to access camera. Please check your camera permissions.</p>
                </div>
            `;
        }
    }
}

// Initialize pose detector when page loads
document.addEventListener('DOMContentLoaded', () => {
    window.poseDetector = new PoseDetector();
});

// Export for use in other scripts
window.PoseDetector = PoseDetector;
641 lines•21.3 KB
javascript
static/js/app.js
Raw Download
Find: Go to:
/**
 * Fitness Coach Bot - Frontend JavaScript
 * Author: RSK World (https://rskworld.in)
 * Founded by: Molla Samser
 * Designer & Tester: Rima Khatun
 * Contact: help@rskworld.in, +91 93305 39277
 * Year: 2026
 */

class FitnessCoachBot {
    constructor() {
        this.init();
        this.loadUserProfile();
        this.loadHealthTips();
        this.updateStats();
    }

    init() {
        // Get DOM elements
        this.chatMessages = document.getElementById('chatMessages');
        this.messageInput = document.getElementById('messageInput');
        this.sendButton = document.getElementById('sendButton');
        this.profileForm = document.getElementById('profileForm');
        this.refreshTipsBtn = document.getElementById('refreshTips');
        
        // Event listeners
        this.sendButton.addEventListener('click', () => this.sendMessage());
        this.messageInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') {
                this.sendMessage();
            }
        });
        
        this.profileForm.addEventListener('submit', (e) => {
            e.preventDefault();
            this.saveUserProfile();
        });
        
        this.refreshTipsBtn.addEventListener('click', () => this.loadHealthTips());
        
        // Quick suggestion buttons
        document.querySelectorAll('.quick-suggestion').forEach(button => {
            button.addEventListener('click', () => {
                this.messageInput.value = button.textContent;
                this.sendMessage();
            });
        });
        
        // Feature cards
        document.querySelectorAll('.feature-card').forEach(card => {
            card.addEventListener('click', () => {
                const feature = card.querySelector('h6').textContent;
                this.handleFeatureClick(feature);
            });
        });
    }

    async sendMessage() {
        const message = this.messageInput.value.trim();
        if (!message) return;

        // Add user message to chat
        this.addMessage(message, 'user');
        this.messageInput.value = '';
        
        // Show typing indicator
        this.showTypingIndicator();
        
        try {
            // Send message to backend
            const response = await fetch('/api/chat', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ message: message })
            });
            
            const data = await response.json();
            
            // Hide typing indicator
            this.hideTypingIndicator();
            
            if (data.success) {
                // Add bot response to chat
                this.addMessage(data.response, 'bot');
            } else {
                this.addMessage('Sorry, I encountered an error. Please try again.', 'bot');
            }
        } catch (error) {
            this.hideTypingIndicator();
            this.addMessage('Sorry, I\'m having trouble connecting. Please check your internet connection.', 'bot');
            console.error('Error sending message:', error);
        }
    }

    addMessage(content, sender) {
        const messageDiv = document.createElement('div');
        messageDiv.className = `message ${sender}-message mb-3 fade-in`;
        
        const avatarClass = sender === 'bot' ? 'bot-avatar' : 'user-avatar';
        const iconClass = sender === 'bot' ? 'fas fa-robot text-primary' : 'fas fa-user text-success';
        
        messageDiv.innerHTML = `
            <div class="d-flex">
                <div class="${avatarClass} me-2">
                    <i class="${iconClass}"></i>
                </div>
                <div class="message-content ${sender === 'user' ? 'bg-primary text-white' : 'bg-light'} rounded p-3">
                    <p class="mb-0">${content}</p>
                    <small class="${sender === 'user' ? 'text-white-50' : 'text-muted'}">${new Date().toLocaleTimeString()}</small>
                </div>
            </div>
        `;
        
        this.chatMessages.appendChild(messageDiv);
        this.scrollToBottom();
    }

    showTypingIndicator() {
        const typingDiv = document.createElement('div');
        typingDiv.className = 'typing-indicator active mb-3';
        typingDiv.id = 'typingIndicator';
        typingDiv.innerHTML = `
            <div class="d-flex">
                <div class="bot-avatar me-2">
                    <i class="fas fa-robot text-primary"></i>
                </div>
                <div class="message-content bg-light rounded p-3">
                    <div class="typing-dots">
                        <span></span>
                        <span></span>
                        <span></span>
                    </div>
                </div>
            </div>
        `;
        
        this.chatMessages.appendChild(typingDiv);
        this.scrollToBottom();
    }

    hideTypingIndicator() {
        const typingIndicator = document.getElementById('typingIndicator');
        if (typingIndicator) {
            typingIndicator.remove();
        }
    }

    scrollToBottom() {
        this.chatMessages.scrollTop = this.chatMessages.scrollHeight;
    }

    async saveUserProfile() {
        const profileData = {
            name: document.getElementById('userName').value,
            age: document.getElementById('userAge').value,
            weight: document.getElementById('userWeight').value,
            fitness_goal: document.getElementById('userGoal').value
        };

        try {
            const response = await fetch('/api/user/profile', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(profileData)
            });
            
            const data = await response.json();
            
            if (data.success) {
                this.showNotification('Profile saved successfully!', 'success');
                this.updateStats();
            } else {
                this.showNotification('Error saving profile. Please try again.', 'error');
            }
        } catch (error) {
            this.showNotification('Error saving profile. Please check your connection.', 'error');
            console.error('Error saving profile:', error);
        }
    }

    async loadUserProfile() {
        try {
            const response = await fetch('/api/user/profile');
            const data = await response.json();
            
            if (data.success && data.user) {
                const user = data.user;
                document.getElementById('userName').value = user.name || '';
                document.getElementById('userAge').value = user.age || '';
                document.getElementById('userWeight').value = user.weight || '';
                document.getElementById('userGoal').value = user.fitness_goal || '';
            }
        } catch (error) {
            console.error('Error loading profile:', error);
        }
    }

    async loadHealthTips() {
        try {
            const response = await fetch('/api/health-tips');
            const data = await response.json();
            
            if (data.success && data.tips) {
                const tipsContainer = document.getElementById('healthTips');
                tipsContainer.innerHTML = '';
                
                data.tips.forEach(tip => {
                    const tipDiv = document.createElement('div');
                    tipDiv.className = 'tip-item mb-2 fade-in';
                    tipDiv.innerHTML = `<small class="text-muted">💡 ${tip.title}</small>`;
                    tipsContainer.appendChild(tipDiv);
                });
            }
        } catch (error) {
            console.error('Error loading health tips:', error);
            // Fallback tips
            this.showFallbackTips();
        }
    }

    showFallbackTips() {
        const fallbackTips = [
            '💡 Drink at least 8 glasses of water daily',
            '🥗 Include protein in every meal',
            '😴 Get 7-9 hours of quality sleep',
            '🏃 Take regular movement breaks',
            '🧘 Practice stress management daily'
        ];
        
        const tipsContainer = document.getElementById('healthTips');
        tipsContainer.innerHTML = '';
        
        fallbackTips.forEach(tip => {
            const tipDiv = document.createElement('div');
            tipDiv.className = 'tip-item mb-2 fade-in';
            tipDiv.innerHTML = `<small class="text-muted">${tip}</small>`;
            tipsContainer.appendChild(tipDiv);
        });
    }

    async updateStats() {
        // Simulated stats - in real app, these would come from backend
        const stats = {
            workouts: Math.floor(Math.random() * 50) + 10,
            streak: Math.floor(Math.random() * 30) + 1,
            calories: Math.floor(Math.random() * 5000) + 1000,
            goals: Math.floor(Math.random() * 20) + 5
        };
        
        // Animate counter updates
        this.animateCounter('workoutCount', stats.workouts);
        this.animateCounter('streakCount', stats.streak);
        this.animateCounter('caloriesCount', stats.calories);
        this.animateCounter('goalsCount', stats.goals);
    }

    animateCounter(elementId, targetValue) {
        const element = document.getElementById(elementId);
        const duration = 1000;
        const step = targetValue / (duration / 16);
        let currentValue = 0;
        
        const timer = setInterval(() => {
            currentValue += step;
            if (currentValue >= targetValue) {
                currentValue = targetValue;
                clearInterval(timer);
            }
            element.textContent = Math.floor(currentValue);
        }, 16);
    }

    handleFeatureClick(feature) {
        const messages = {
            'Workout Plans': 'Can you create a personalized workout plan for me?',
            'Exercise Guidance': 'Show me proper form for basic exercises',
            'Progress Tracking': 'How can I track my fitness progress effectively?',
            'Health Tips': 'Give me some nutrition and health advice'
        };
        
        this.messageInput.value = messages[feature] || `Tell me about ${feature}`;
        this.sendMessage();
    }

    showNotification(message, type) {
        const notification = document.createElement('div');
        notification.className = `${type}-message position-fixed top-0 start-50 translate-middle-x mt-3`;
        notification.style.zIndex = '9999';
        notification.innerHTML = `
            <div class="d-flex align-items-center">
                <i class="fas ${type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'} me-2"></i>
                ${message}
            </div>
        `;
        
        document.body.appendChild(notification);
        
        setTimeout(() => {
            notification.remove();
        }, 3000);
    }
}

// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
    new FitnessCoachBot();
});

// Service Worker for PWA capabilities (optional)
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/sw.js')
            .then((registration) => {
                console.log('SW registered: ', registration);
            })
            .catch((registrationError) => {
                console.log('SW registration failed: ', registrationError);
            });
    });
}
325 lines•11.7 KB
javascript
🚀 Support RSK World

Subscribe to our YouTube channel for latest tutorials & updates!



Click subscribe & support our work ❤️

About RSK World

Founded by Molla Samser, with Designer & Tester Rima Khatun, RSK World is your one-stop destination for free programming resources, source code, and development tools.

Founder: Molla Samser
Designer & Tester: Rima Khatun

Development

  • Game Development
  • Web Development
  • Mobile Development
  • AI Development
  • Development Tools

Legal

  • Terms & Conditions
  • Privacy Policy
  • Disclaimer

Contact Info

Nutanhat, Mongolkote
Purba Burdwan, West Bengal
India, 713147

+91 93305 39277

hello@rskworld.in
support@rskworld.in

© 2026 RSK World. All rights reserved.

Content used for educational purposes only. View Disclaimer