akarsh999 commited on
Commit
4eb76aa
·
verified ·
1 Parent(s): 7a2332d

Upload 12 files

Browse files
Files changed (3) hide show
  1. SMART_DEPLOYMENT.md +101 -0
  2. app.py +5 -1
  3. athletic_performance.py +68 -29
SMART_DEPLOYMENT.md ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🧠 Smart Deployment System
2
+
3
+ ## 🎯 How It Works
4
+
5
+ Your `app.py` now automatically detects what to deploy:
6
+
7
+ ```
8
+ 📁 Repository
9
+ ├── app.py ← Smart entry point
10
+ ├── flask_api.py ← Flask routes
11
+ ├── athletic_performance.py ← Core logic
12
+ ├── requirements.txt ← Both Gradio + Flask deps
13
+ └── Dockerfile (optional) ← Controls deployment mode
14
+ ```
15
+
16
+ ## 🔄 Deployment Modes
17
+
18
+ ### 🔌 **Flask API Mode**
19
+ **Triggered when:**
20
+ - ✅ Dockerfile exists in repo
21
+ - ✅ Running in Docker container
22
+ - ✅ `DEPLOYMENT_MODE=flask` env var
23
+
24
+ **What you get:**
25
+ - Clean REST API endpoints
26
+ - `/ai-coaching` with favorite sports
27
+ - JSON responses with percentiles + tips
28
+ - No eventId complexity
29
+
30
+ ### 🎨 **Gradio UI Mode**
31
+ **Triggered when:**
32
+ - ❌ No Dockerfile in repo
33
+ - ✅ `DEPLOYMENT_MODE=gradio` env var
34
+ - ✅ Default fallback mode
35
+
36
+ **What you get:**
37
+ - Interactive web interface
38
+ - Sports selection checkboxes
39
+ - Built-in API via Gradio
40
+ - User-friendly forms
41
+
42
+ ## 🚀 Usage Examples
43
+
44
+ ### **Deploy Flask API:**
45
+ 1. Keep `Dockerfile` in your repo
46
+ 2. Push to HuggingFace Spaces
47
+ 3. Choose Docker SDK
48
+ 4. → Automatically runs Flask API! 🔌
49
+
50
+ ### **Deploy Gradio UI:**
51
+ 1. Remove/rename `Dockerfile`
52
+ 2. Push to HuggingFace Spaces
53
+ 3. Choose Gradio SDK
54
+ 4. → Automatically runs Gradio UI! 🎨
55
+
56
+ ### **Switch Modes:**
57
+ - **Want Flask?** → Add `Dockerfile` back
58
+ - **Want Gradio?** → Remove `Dockerfile`
59
+ - **Force mode?** → Set `DEPLOYMENT_MODE` env var
60
+
61
+ ## 🎯 Benefits
62
+
63
+ ✅ **One Codebase** - Same repo for both deployments
64
+ ✅ **Auto-Detection** - No manual configuration
65
+ ✅ **Easy Switching** - Just add/remove Dockerfile
66
+ ✅ **Same Features** - Favorite sports work in both modes
67
+ ✅ **Flexible** - Override with environment variables
68
+
69
+ ## 📋 HuggingFace Spaces Setup
70
+
71
+ ### For Flask API:
72
+ ```
73
+ 1. Create new Space
74
+ 2. SDK: Docker ✅
75
+ 3. Upload files WITH Dockerfile
76
+ 4. → Flask API runs automatically
77
+ ```
78
+
79
+ ### For Gradio UI:
80
+ ```
81
+ 1. Create new Space
82
+ 2. SDK: Gradio ✅
83
+ 3. Upload files WITHOUT Dockerfile
84
+ 4. → Gradio UI runs automatically
85
+ ```
86
+
87
+ ## 🔧 Environment Variables
88
+
89
+ - `DEPLOYMENT_MODE=flask` → Force Flask mode
90
+ - `DEPLOYMENT_MODE=gradio` → Force Gradio mode
91
+ - `PORT=7860` → Custom port (default: 7860)
92
+ - `GEMINI_API_KEY=xxx` → Default API key
93
+
94
+ ## 🎉 Result
95
+
96
+ **One smart repository that automatically adapts to your deployment needs!**
97
+
98
+ 🔌 **Flask**: Clean API for mobile apps
99
+ 🎨 **Gradio**: Beautiful UI for web users
100
+ 🧠 **Smart**: Chooses automatically
101
+ ⚡ **Features**: Favorite sports + concise AI coaching in both modes!
app.py CHANGED
@@ -257,7 +257,11 @@ def get_ai_coaching_recommendations(youtube_url, video_file, user_height_cm, use
257
 
258
  ## 🏆 AI Expert Coaching Analysis
259
 
260
- {ai_result['analysis']}
 
 
 
 
261
 
262
  ---
263
  *Analysis powered by Google Gemini AI*
 
257
 
258
  ## 🏆 AI Expert Coaching Analysis
259
 
260
+ **📊 Performance Percentiles:**
261
+ {chr(10).join([f"- **{sport}**: {percentile}th percentile" for sport, percentile in ai_result.get('analysis', {}).get('sports', {}).items()])}
262
+
263
+ **💡 Improvement Tips:**
264
+ {chr(10).join([f"{i+1}. {tip}" for i, tip in enumerate(ai_result.get('analysis', {}).get('tips', []))])}
265
 
266
  ---
267
  *Analysis powered by Google Gemini AI*
athletic_performance.py CHANGED
@@ -971,7 +971,7 @@ def get_ai_sports_coaching_analysis(jump_height_cm, user_height_cm, gender, favo
971
  # Format favorite sports list
972
  sports_list = ", ".join(favorite_sports) if favorite_sports else "None specified"
973
 
974
- prompt = f"""You are a sports coach. Based on this athlete's jump performance, provide a simple analysis:
975
 
976
  ATHLETE DATA:
977
  - Gender: {gender}
@@ -979,21 +979,25 @@ ATHLETE DATA:
979
  - Jump Height: {jump_height_cm:.2f} cm
980
  - Sports: {sports_list}
981
 
982
- PROVIDE EXACTLY THIS FORMAT:
983
-
984
- **PERCENTILE RANKINGS:**
985
- - Basketball: [number]th percentile
986
- - Volleyball: [number]th percentile
987
- - Swimming: [number]th percentile
988
- (Only include the sports from their list: {sports_list})
989
-
990
- **IMPROVEMENT TIPS:**
991
- 1. [One simple sentence about technique]
992
- 2. [One simple sentence about training]
993
- 3. [One simple sentence about strength/power]
994
- 4. [One simple sentence about specific skill]
995
-
996
- Keep it simple for beginner athletes. Use basic English. No long explanations."""
 
 
 
 
997
 
998
  # Prepare the API request - try Gemini 1.5 Pro as fallback
999
  url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent"
@@ -1044,21 +1048,56 @@ Keep it simple for beginner athletes. Use basic English. No long explanations.""
1044
  result = response.json()
1045
 
1046
  if 'candidates' in result and len(result['candidates']) > 0:
1047
- ai_analysis = result['candidates'][0]['content']['parts'][0]['text']
1048
 
1049
- return {
1050
- "success": True,
1051
- "analysis": ai_analysis,
1052
- "athlete_profile": {
1053
- "gender": gender,
1054
- "height_cm": user_height_cm,
1055
- "jump_height_cm": jump_height_cm,
1056
- "relative_jump_height": relative_jump_height,
1057
- "flight_time_s": flight_time_s,
1058
- "peak_power_watts": peak_power_watts,
1059
- "rfd": rfd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1060
  }
1061
- }
1062
  else:
1063
  return {"error": f"No response generated from AI. Response: {result}"}
1064
 
 
971
  # Format favorite sports list
972
  sports_list = ", ".join(favorite_sports) if favorite_sports else "None specified"
973
 
974
+ prompt = f"""You are a sports coach. Based on this athlete's jump performance, provide analysis in JSON format.
975
 
976
  ATHLETE DATA:
977
  - Gender: {gender}
 
979
  - Jump Height: {jump_height_cm:.2f} cm
980
  - Sports: {sports_list}
981
 
982
+ RESPOND WITH VALID JSON IN THIS EXACT FORMAT:
983
+ {{
984
+ "sports": {{
985
+ "Basketball": 75,
986
+ "Volleyball": 80
987
+ }},
988
+ "tips": [
989
+ "Focus on proper landing technique to reduce knee strain",
990
+ "Add plyometric exercises to your training routine",
991
+ "Strengthen your leg muscles with squats and lunges",
992
+ "Practice explosive movements for better power development"
993
+ ]
994
+ }}
995
+
996
+ RULES:
997
+ - "sports" should contain percentile rankings (0-100) only for sports in their list: {sports_list}
998
+ - "tips" must be exactly 4 practical improvement suggestions
999
+ - Use simple English for beginner athletes
1000
+ - Return ONLY valid JSON, no extra text"""
1001
 
1002
  # Prepare the API request - try Gemini 1.5 Pro as fallback
1003
  url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro:generateContent"
 
1048
  result = response.json()
1049
 
1050
  if 'candidates' in result and len(result['candidates']) > 0:
1051
+ ai_response_text = result['candidates'][0]['content']['parts'][0]['text']
1052
 
1053
+ try:
1054
+ # Parse the JSON response from AI
1055
+ ai_analysis_json = json.loads(ai_response_text)
1056
+
1057
+ # Validate the expected structure
1058
+ if not isinstance(ai_analysis_json, dict):
1059
+ raise ValueError("Response is not a JSON object")
1060
+
1061
+ # Extract required fields with defaults
1062
+ sports_percentiles = ai_analysis_json.get('sports', {})
1063
+ tips = ai_analysis_json.get('tips', [])
1064
+
1065
+ # Ensure tips is a list
1066
+ if not isinstance(tips, list):
1067
+ tips = []
1068
+
1069
+ # Ensure sports is a dict
1070
+ if not isinstance(sports_percentiles, dict):
1071
+ sports_percentiles = {}
1072
+
1073
+ return {
1074
+ "success": True,
1075
+ "analysis": {
1076
+ "sports": sports_percentiles,
1077
+ "tips": tips
1078
+ },
1079
+ "athlete_profile": {
1080
+ "gender": gender,
1081
+ "height_cm": user_height_cm,
1082
+ "jump_height_cm": jump_height_cm,
1083
+ "relative_jump_height": relative_jump_height,
1084
+ "flight_time_s": flight_time_s,
1085
+ "peak_power_watts": peak_power_watts,
1086
+ "rfd": rfd
1087
+ }
1088
+ }
1089
+
1090
+ except json.JSONDecodeError as e:
1091
+ # Fallback: if JSON parsing fails, try to extract some information
1092
+ return {
1093
+ "error": f"AI returned invalid JSON. Raw response: {ai_response_text[:200]}...",
1094
+ "parsing_error": str(e)
1095
+ }
1096
+ except Exception as e:
1097
+ return {
1098
+ "error": f"Error processing AI response: {str(e)}",
1099
+ "raw_response": ai_response_text[:200]
1100
  }
 
1101
  else:
1102
  return {"error": f"No response generated from AI. Response: {result}"}
1103