HEMANTH commited on
Commit
abc9746
·
1 Parent(s): e517fec

first all files

Browse files
Files changed (8) hide show
  1. BasicPoseModule.py +133 -0
  2. Dockerfile +22 -0
  3. app.py +490 -0
  4. diet_plan.py +145 -0
  5. feed_back_llm.py +68 -0
  6. main.py +127 -0
  7. requirements.txt +7 -0
  8. serviceAccountKey.json +14 -0
BasicPoseModule.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import mediapipe as mp
3
+ import math
4
+
5
+ class PoseDetectorModified():
6
+
7
+ def _init_(self, mode=False, complexity=1, smooth_landmarks=True,
8
+ enable_segmentation=False, smooth_segmentation=True,
9
+ detectionCon=0.5, trackCon=0.5):
10
+ """
11
+ Initializes a new instance of the PoseDetectorModified class.
12
+
13
+ Args:
14
+ mode (bool): Whether to use the upper-body-only pose landmark model or the full-body pose landmark model.
15
+ complexity (int): Complexity of the pose landmark model (must be between 0 and 2).
16
+ smooth_landmarks (bool): Whether to smooth the pose landmarks or not.
17
+ enable_segmentation (bool): Whether to enable person segmentation or not.
18
+ smooth_segmentation (bool): Whether to smooth the person segmentation or not.
19
+ detectionCon (float): Minimum confidence value (between 0 and 1) for the detection to be considered successful.
20
+ trackCon (float): Minimum confidence value (between 0 and 1) for the tracking to be considered successful.
21
+ """
22
+
23
+ self.mode = mode
24
+ self.complexity = complexity
25
+ self.smooth_landmarks = smooth_landmarks
26
+ self.enable_segmentation = enable_segmentation
27
+ self.smooth_segmentation = smooth_segmentation
28
+ self.detectionCon = detectionCon
29
+ self.trackCon = trackCon
30
+
31
+ self.mpDraw = mp.solutions.drawing_utils
32
+ self.mpPose = mp.solutions.pose
33
+ self.pose = self.mpPose.Pose(self.mode, self.complexity, self.smooth_landmarks,
34
+ self.enable_segmentation, self.smooth_segmentation,
35
+ self.detectionCon, self.trackCon)
36
+
37
+ def find_pose(self, img, draw=True):
38
+ """
39
+ Finds the pose landmarks and connections in an image or a video frame.
40
+
41
+ Args:
42
+ img (numpy.ndarray): The input image or video frame.
43
+ draw (bool): Whether to draw the pose landmarks and connections on the image or not.
44
+
45
+ Returns:
46
+ The input image with the pose landmarks and connections drawn on it (if draw is True).
47
+ """
48
+ imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
49
+ self.results = self.pose.process(imgRGB)
50
+
51
+ if self.results.pose_landmarks:
52
+ if draw:
53
+ self.mpDraw.draw_landmarks(img, self.results.pose_landmarks,
54
+ self.mpPose.POSE_CONNECTIONS)
55
+
56
+ return img
57
+
58
+ def find_position(self, img, draw=True):
59
+ """
60
+ Finds the pose landmark positions in an image or a video frame.
61
+
62
+ Args:
63
+ img (numpy.ndarray): The input image or video frame.
64
+ draw (bool): Whether to draw the pose landmark positions on the image or not.
65
+
66
+ Returns:
67
+ A list containing the landmark ID, X and Y positions for each landmark in the pose.
68
+ """
69
+ lm_list = []
70
+ if self.results.pose_landmarks:
71
+ for id, lm in enumerate(self.results.pose_landmarks.landmark):
72
+ h, w, c = img.shape
73
+ cx, cy = int(lm.x * w), int(lm.y * h)
74
+ lm_list.append([id, cx, cy])
75
+ if draw:
76
+ cv2.circle(img, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
77
+ return lm_list
78
+
79
+ def find_angle(self, img, p1, p2, p3, lm_list, draw=True):
80
+ """
81
+ Calculates the angle between three landmarks.
82
+
83
+ Args:
84
+ img (numpy.ndarray): The input image or video frame.
85
+ p1 (int): ID of the first landmark.
86
+ p2 (int): ID of the second landmark (vertex of the angle).
87
+ p3 (int): ID of the third landmark.
88
+ lm_list (list): List of landmarks with their coordinates.
89
+ draw (bool): Whether to draw the angle on the image or not.
90
+
91
+ Returns:
92
+ float: The calculated angle in degrees.
93
+ """
94
+ x1, y1 = lm_list[p1][1:]
95
+ x2, y2 = lm_list[p2][1:]
96
+ x3, y3 = lm_list[p3][1:]
97
+
98
+ # Calculate the angle
99
+ angle = math.degrees(math.atan2(y3 - y2, x3 - x2) - math.atan2(y1 - y2, x1 - x2))
100
+ angle = abs(angle)
101
+ if angle > 180:
102
+ angle = 360 - angle
103
+
104
+ # Draw the angle on the image
105
+ if draw:
106
+ cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 3)
107
+ cv2.line(img, (x3, y3), (x2, y2), (255, 255, 255), 3)
108
+ cv2.circle(img, (x1, y1), 10, (0, 0, 255), cv2.FILLED)
109
+ cv2.circle(img, (x2, y2), 10, (0, 0, 255), cv2.FILLED)
110
+ cv2.circle(img, (x3, y3), 10, (0, 0, 255), cv2.FILLED)
111
+ cv2.putText(img, str(int(angle)), (x2 - 50, y2 + 50),
112
+ cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 255), 2)
113
+
114
+ return angle
115
+
116
+ def main():
117
+ detector = PoseDetectorModified()
118
+ cap = cv2.VideoCapture(0)
119
+ while cap.isOpened():
120
+ ret, img = cap.read()
121
+ if ret:
122
+ img = detector.find_pose(img)
123
+ lm_list = detector.find_position(img, False)
124
+ if len(lm_list) != 0:
125
+ angle = detector.find_angle(img, 11, 13, 15, lm_list)
126
+ print(angle)
127
+ cv2.imshow('Pose Detection', img)
128
+ if cv2.waitKey(10) & 0xFF == ord('q'):
129
+ break
130
+
131
+ cap.release()
132
+ cv2.destroyAllWindows()
133
+
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.9
5
+
6
+ # Install system dependencies for OpenCV
7
+ RUN apt-get update && apt-get install -y \
8
+ libgl1-mesa-glx \
9
+ libglib2.0-0 \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ RUN useradd -m -u 1000 user
13
+ USER user
14
+ ENV PATH="/home/user/.local/bin:$PATH"
15
+
16
+ WORKDIR /app
17
+
18
+ COPY --chown=user ./requirements.txt requirements.txt
19
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
20
+
21
+ COPY --chown=user . /app
22
+ CMD ["gunicorn", "-b", "0.0.0.0:7860","app:app"]
app.py ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, redirect, url_for, render_template, session, Response
2
+ import firebase_admin
3
+ from firebase_admin import credentials, auth, db
4
+ # from workouts.bicepCurls import bicepcurls
5
+ import os
6
+ import time
7
+ import mediapipe as mp
8
+ import cv2
9
+ import numpy as np
10
+
11
+ today_date = time.strftime("%Y-%m-%d")
12
+ mp_pose = mp.solutions.pose
13
+ mp_drawing = mp.solutions.drawing_utils
14
+ curr_track = {}
15
+ # Initialize Flask app
16
+ app = Flask(__name__)
17
+ app.secret_key = '9177'
18
+ # Firebase Admin SDK Initialization
19
+ cred = credentials.Certificate("serviceAccountKey.json")
20
+ firebase_admin.initialize_app(cred, {
21
+ 'databaseURL': 'https://fitnessdb-c9b11-default-rtdb.firebaseio.com/'
22
+ })
23
+
24
+
25
+
26
+ def calculate_angle(a, b, c):
27
+ a = np.array(a) # First point
28
+ b = np.array(b) # Middle point
29
+ c = np.array(c) # Last point
30
+
31
+ radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
32
+ angle = np.abs(radians * 180.0 / np.pi)
33
+
34
+ if angle > 180.0:
35
+ angle = 360 - angle
36
+
37
+ return angle
38
+ stop_processing = False # Global flag to control video stream
39
+
40
+ def is_shoulder_press_correct(landmarks, mp_pose):
41
+ # Get coordinates of shoulder, elbow, and wrist (left arm as example)
42
+ shoulder = [landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x,
43
+ landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y]
44
+ elbow = [landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x,
45
+ landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y]
46
+ wrist = [landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x,
47
+ landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y]
48
+
49
+ # Calculate angle at the elbow (shoulder, elbow, wrist)
50
+ elbow_angle = calculate_angle(shoulder, elbow, wrist)
51
+
52
+ # Check if the motion is vertical (wrist higher than elbow)
53
+ if wrist[1] < elbow[1] and elbow[1] < shoulder[1]:
54
+ # Ensure proper angle range for a shoulder press
55
+ if 160 <= elbow_angle <= 180:
56
+ return "Shoulder Press: Correct", True
57
+ else:
58
+ return "Shoulder Press: Incorrect - Elbow angle", False
59
+ else:
60
+ return "Shoulder Press: Incorrect - Alignment", False
61
+
62
+ # Video processing function
63
+ def shoulder_press_count(video_path):
64
+ global curr_track
65
+ cap = cv2.VideoCapture(video_path)
66
+ count = 0
67
+ rep_started = False
68
+
69
+ with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:
70
+ while cap.isOpened():
71
+ ret, frame = cap.read()
72
+ if not ret:
73
+ break
74
+
75
+ # Convert the frame to RGB
76
+ image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
77
+ image.flags.writeable = False
78
+
79
+ # Process the image for pose detection
80
+ results = pose.process(image)
81
+
82
+ # Convert back to BGR for rendering
83
+ image.flags.writeable = True
84
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
85
+
86
+ # Extract landmarks
87
+ if results.pose_landmarks:
88
+ landmarks = results.pose_landmarks.landmark
89
+
90
+ # Check shoulder press posture
91
+ feedback, is_correct = is_shoulder_press_correct(landmarks, mp_pose)
92
+
93
+ # Count reps
94
+ if is_correct and not rep_started:
95
+ rep_started = True
96
+ elif not is_correct and rep_started:
97
+ rep_started = False
98
+ count += 1
99
+
100
+ # Display feedback
101
+ cv2.putText(image, feedback, (50, 50),
102
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0) if is_correct else (0, 0, 255), 2, cv2.LINE_AA)
103
+
104
+ # Display rep count
105
+ cv2.putText(image, f'Reps: {count}', (50, 100),
106
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
107
+
108
+ # Draw landmarks
109
+ mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
110
+
111
+ else:
112
+ # Warn if no landmarks are detected
113
+ cv2.putText(image, "No body detected", (50, 50),
114
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
115
+
116
+ # Encode frame for streaming
117
+ ret, buffer = cv2.imencode('.jpg', image)
118
+ frame = buffer.tobytes()
119
+ yield (b'--frame\r\n'
120
+ b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
121
+
122
+ cap.release()
123
+ curr_track['shoulderpress'] = count
124
+ print("shoulder_press count :",count)
125
+ def process_bicep_curls(video_path):
126
+ global curr_track
127
+ global stop_processing # Access the global flag to stop the stream
128
+ cap = cv2.VideoCapture(video_path)
129
+ count = 0
130
+ movement_dir = 0
131
+
132
+ # Initialize MediaPipe Pose
133
+ with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:
134
+ while cap.isOpened():
135
+ if stop_processing: # If stop_processing flag is set, break the loop
136
+ break
137
+ ret, frame = cap.read()
138
+ if not ret:
139
+ break
140
+
141
+ frame = cv2.resize(frame, (1280, 720))
142
+
143
+ # Convert the frame to RGB
144
+ image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
145
+ image.flags.writeable = False
146
+
147
+ # Process the image for pose detection
148
+ results = pose.process(image)
149
+
150
+ # Convert back to BGR for rendering
151
+ image.flags.writeable = True
152
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
153
+
154
+ if results.pose_landmarks:
155
+ landmarks = results.pose_landmarks.landmark
156
+
157
+ # Extract relevant joints
158
+ shoulder = [landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x,
159
+ landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y]
160
+ elbow = [landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x,
161
+ landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y]
162
+ wrist = [landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x,
163
+ landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y]
164
+
165
+ # Calculate elbow angle
166
+ elbow_angle = calculate_angle(shoulder, elbow, wrist)
167
+
168
+ # Determine movement direction and count reps
169
+ progress_percentage = np.interp(elbow_angle, (50, 160), (0, 100))
170
+ progress_bar = np.interp(elbow_angle, (50, 160), (650, 100))
171
+
172
+ color = (255, 0, 255)
173
+ if progress_percentage == 100:
174
+ color = (0, 255, 0)
175
+ if movement_dir == 0:
176
+ count += 0.5
177
+ movement_dir = 1
178
+ if progress_percentage == 0:
179
+ color = (0, 0, 255)
180
+ if movement_dir == 1:
181
+ count += 0.5
182
+ movement_dir = 0
183
+
184
+ # Draw Progress Bar
185
+ cv2.rectangle(frame, (1100, 100), (1175, 650), color, 3)
186
+ cv2.rectangle(frame, (1100, int(progress_bar)), (1175, 650), color, cv2.FILLED)
187
+ cv2.putText(frame, f'{int(progress_percentage)}%', (1100, 75), cv2.FONT_HERSHEY_PLAIN, 4, color, 4)
188
+
189
+ # Draw Counter
190
+ cv2.rectangle(frame, (0, 450), (250, 720), (0, 255, 0), cv2.FILLED)
191
+ cv2.putText(frame, str(int(count)), (45, 670), cv2.FONT_HERSHEY_PLAIN, 15, (255, 0, 0), 30)
192
+
193
+ # Draw landmarks
194
+ mp_drawing.draw_landmarks(frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
195
+
196
+ # Draw Finish Button on the video
197
+ cv2.rectangle(frame, (10, 10), (150, 60), (0, 0, 255), -1) # Red button
198
+ cv2.putText(frame, "FINISH", (20, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
199
+
200
+ # Encode the frame for streaming
201
+ ret, buffer = cv2.imencode('.jpg', frame)
202
+ frame = buffer.tobytes()
203
+
204
+ yield (b'--frame\r\n'
205
+ b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
206
+
207
+ cap.release()
208
+ curr_track['bicepcurls'] = count
209
+
210
+ # Flask route to stop the video feed
211
+ @app.route('/stop_stream', methods=['POST'])
212
+ def stop_stream():
213
+ global stop_processing
214
+ stop_processing = True # Stop the video stream when this route is hit
215
+ return "Stream stopped", 200
216
+
217
+
218
+ @app.route('/')
219
+ def index():
220
+ return render_template('index.html')
221
+ # Login Route
222
+ @app.route('/register', methods=['POST'])
223
+ def register():
224
+ data = request.get_json()
225
+ session['progress'] = {}
226
+ session['recommended_plan'] = ""
227
+
228
+ # Extract user details from the request
229
+ name = data['name']
230
+ email = data['email']
231
+ password = data['password']
232
+ age = data['age']
233
+ height = data['height']
234
+ weight = data['weight']
235
+ max_pullups = data['maxPullups']
236
+ min_pullups = data['minPullups']
237
+ gender = data['gender']
238
+ pushups = data.get('pushups',[])
239
+ squats = data.get('squats',[])
240
+ pullups = data.get('pullups',[])
241
+ shoulderpress = data.get('shoulderpress',[])
242
+ bicepcurls = data.get('bicepcurls',[])
243
+
244
+ track = {
245
+ "pushups": pushups,
246
+ "squats": squats,
247
+ "pullups": pullups,
248
+ 'shoulder_press' : shoulderpress,
249
+ 'bicepcurls':bicepcurls,
250
+ 'recommended_plan':""
251
+ }
252
+
253
+
254
+ session['progress'] = track
255
+ # bicepcurls = data.get()
256
+
257
+
258
+ # Create Firebase user using Firebase Auth
259
+ try:
260
+ # Create the user in Firebase Authentication
261
+ user = auth.create_user(
262
+ email=email,
263
+ password=password,
264
+ display_name=name
265
+ )
266
+
267
+ # Store additional user data in Firebase Realtime Database
268
+ user_ref = db.reference(f'users/{user.uid}')
269
+ user_ref.set({
270
+ 'name': name,
271
+ 'email': email,
272
+ 'age': age,
273
+ 'height': height,
274
+ 'weight': weight,
275
+ 'maxPullups': max_pullups,
276
+ 'minPullups': min_pullups,
277
+ 'gender': gender,
278
+ 'progress':track
279
+ })
280
+
281
+
282
+
283
+ return jsonify({"message": "User registered successfully!"}), 200
284
+ except Exception as e:
285
+ return jsonify({"error": str(e)}), 400
286
+
287
+ @app.route('/login', methods=['POST'])
288
+ def login():
289
+ data = request.get_json()
290
+
291
+ email = data['email']
292
+ password = data['password']
293
+
294
+ try:
295
+ # Authenticate user with Firebase Auth
296
+ user = auth.get_user_by_email(email)
297
+
298
+ # Validate password (Firebase Authentication handles password validation)
299
+ # No need to manually validate password here since Firebase Authentication ensures the password matches
300
+ ref = db.reference("users")
301
+ users_data = ref.get()
302
+
303
+ for user_id,user_data in users_data.items():
304
+ if user_data.get('email') == email:
305
+ user_name = user_data['name']
306
+ session['user_details'] = user_data
307
+ session['progress'] = user_data['progress']
308
+ # session['progress'] = user_data.get('progress',{})
309
+
310
+ print(user_data['name'])
311
+
312
+
313
+ return jsonify({"message":"sucess"})
314
+
315
+ except Exception as e:
316
+ return jsonify({"error": "Invalid credentials"}), 401
317
+
318
+ # Home Route (Display User Credentials)
319
+ @app.route('/home', methods=['GET'])
320
+ def home():
321
+ # Get the ID token from the query parameter
322
+ progress = session['progress']
323
+
324
+ print(progress)
325
+
326
+ sum = 0
327
+ length = 0
328
+ try:
329
+ sum += progress['pushups'][-1]
330
+ except:
331
+ sum += 0
332
+
333
+ # progress = {"pushups":12}
334
+ session[today_date] = {"bicepcurls":0,"squats":0,"pushups":0,"shoulderpress":0,"pullups":0}
335
+
336
+ available_plans =[
337
+ {"name": "fitness maintenance", "description": "Build strength and muscle mass."},
338
+ {"name": "weight loss", "description": "Lose weight through healthy diet and exercise."},
339
+
340
+ ]
341
+
342
+ # Process plan names in Python
343
+ for plan in available_plans:
344
+ # Use dictionary key 'name' instead of accessing as an attribute
345
+ plan['name'] = plan['name'].replace(' ', '-').lower()
346
+
347
+ # Define a recommended plan
348
+
349
+ # Now pass the modified data to the template
350
+
351
+ print(sum)
352
+
353
+
354
+ if sum == 0:
355
+ height = int(session['user_details']['height'])
356
+ weight = int(session['user_details']['weight'])
357
+
358
+ bmi = weight / (height/100)**2
359
+
360
+ recomm = ""
361
+
362
+ if bmi < 18.5:
363
+ recomm = "weight gain"
364
+ available_plans.pop(2)
365
+ elif 18.5 <= bmi <=24.9:
366
+ recomm = "fitness maintenance"
367
+ available_plans.pop(0)
368
+ else:
369
+ recomm = "weight loss"
370
+ available_plans.pop(1)
371
+
372
+ session['fitness_plan'] = recomm
373
+
374
+
375
+ # Recommended workout code goes here
376
+ return render_template('workout.html', recommended_plan=recomm, available_plans=available_plans)
377
+ else:
378
+
379
+ return render_template("currentprogress.html",sum=(sum/3) * 100,recommended_plan=progress["recommended_plan"])
380
+ @app.route('/upload-video/<exercise_name>', methods=['POST'])
381
+ def upload_video(exercise_name):
382
+ if exercise_name == 'bicepcurls':
383
+ if 'video' not in request.files:
384
+ return "No video file uploaded", 400
385
+
386
+ video = request.files['video']
387
+
388
+ if video.filename == '':
389
+ return "No selected file", 400
390
+
391
+ # Save the uploaded video with a unique name
392
+ video_filename = f"{exercise_name}_{video.filename}"
393
+ video_path = os.path.join("uploads", video_filename)
394
+ video.save(video_path)
395
+
396
+ return Response(process_bicep_curls(video_path), mimetype="multipart/x-mixed-replace; boundary=frame")
397
+ elif exercise_name == "shoulderpress":
398
+ if 'video' not in request.files:
399
+ return "No video file uploaded", 400
400
+
401
+ video = request.files['video']
402
+
403
+ if video.filename == '':
404
+ return "No selected file", 400
405
+
406
+ # Save the uploaded video with a unique name
407
+ video_filename = f"{exercise_name}_{video.filename}"
408
+ video_path = os.path.join("uploads", video_filename)
409
+ video.save(video_path)
410
+
411
+ return Response(shoulder_press_count(video_path), mimetype="multipart/x-mixed-replace; boundary=frame")
412
+ @app.route('/diet-data')
413
+ def diet_data():
414
+ # Assuming the JSON file is stored in the static folder
415
+ try:
416
+ with open(os.path.join(app.static_folder, 'diet.json'), 'r') as f:
417
+ diet_data = f.read()
418
+ return diet_data
419
+ except FileNotFoundError:
420
+ return jsonify({"error": "Diet data not found."}), 404
421
+ @app.route('/diet')
422
+ def diet():
423
+
424
+ return render_template("diet.html")
425
+
426
+
427
+ from feed_back_llm import update_feed_back
428
+
429
+ @app.route("/daily-report")
430
+ def daily_report():
431
+ global curr_track
432
+ print(curr_track)
433
+ planned_reps = 8
434
+ planned_resttime = 1
435
+ counted_restime = 2
436
+
437
+ update_feed_back()
438
+
439
+
440
+ return render_template("feed_back.html")
441
+
442
+ @app.route('/weight-gain')
443
+ def weight_gain():
444
+
445
+ workouts = [
446
+ {"name":"bicepcurls", "reps":12},
447
+ {"name":"shoulderpress", "reps":10},
448
+ {"name":"pullups", "reps":10},
449
+ {"name":"squats", "reps":15},
450
+ ]
451
+
452
+
453
+ return render_template("workouts_List.html",exercises=workouts)
454
+ @app.route('/weight-loss')
455
+ def weight_loss():
456
+ workouts = [
457
+ {"name":"bicepcurls", "reps":12},
458
+ {"name":"shoulderpress", "reps":10},
459
+ {"name":"pullups", "reps":10},
460
+ {"name":"squats", "reps":15},
461
+ ]
462
+
463
+
464
+ return render_template("workouts_List.html",exercises=workouts)
465
+ @app.route('/progress')
466
+ def progress():
467
+
468
+ return render_template("currentprogress.html",sum=70)
469
+
470
+ @app.route('/fitness-maintenance')
471
+ def fitness_maintenance():
472
+ workouts = [
473
+ {"name":"bicepcurls", "reps":12},
474
+ {"name":"shoulderpress", "reps":10},
475
+ {"name":"pullups", "reps":10},
476
+ {"name":"squats", "reps":15},
477
+ ]
478
+
479
+
480
+ return render_template("workouts_List.html",exercises=workouts)
481
+ if __name__ == '__main__':
482
+ app.run(debug=True)
483
+
484
+
485
+
486
+
487
+
488
+
489
+
490
+
diet_plan.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ from langchain_core.output_parsers import StrOutputParser
5
+ from langchain_groq import ChatGroq
6
+ from langchain_core.messages import HumanMessage, SystemMessage
7
+ import firebase_admin
8
+ from firebase_admin import credentials, auth, db
9
+ from flask import Flask, request, jsonify, redirect, url_for, render_template, session, Response
10
+
11
+
12
+ cred = credentials.Certificate("serviceAccountKey.json")
13
+ firebase_admin.initialize_app(cred, {
14
+ 'databaseURL': 'https://fitnessdb-c9b11-default-rtdb.firebaseio.com/'
15
+ })
16
+
17
+ def predict_diet_plan():
18
+ # Load environment variables
19
+ load_dotenv()
20
+ groq_api_key = "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"
21
+
22
+ # Initialize the model
23
+ model = ChatGroq(model="Gemma2-9b-It", groq_api_key=groq_api_key)
24
+
25
+ # User input fields for personalized diet details
26
+ print("Personalized Diet Plan Generator")
27
+
28
+ # User inputs
29
+ ref = db.reference("users")
30
+ users_data = ref.get()
31
+ mail = 'terlihemanth21@gmail.com'
32
+ for user_id, user_data in users_data.items():
33
+ if user_data.get('email') == mail:
34
+ age = 20 # Default minimum age
35
+ height = user_data.get('height') # Default height in cm
36
+ weight = user_data.get('weight') # Default weight in kg
37
+ gender = user_data.get('gender')
38
+ activity_level = user_data.get('activity_level', 'Sedentary')
39
+ goal = user_data.get('goal', 'Maintain Fitness')
40
+ health_conditions = user_data.get('health_conditions', 'None')
41
+ gut_problems = user_data.get('gut_problems', 'None')
42
+ dietary_preferences = user_data.get('dietary_preferences', 'Non-Vegetarian')
43
+ food_restrictions = user_data.get('food_restrictions', 'None')
44
+ location_preferences = user_data.get('location_preferences', 'Home-Cooked Food')
45
+ breakfast = user_data.get('breakfast', 'Default Breakfast')
46
+ lunch = user_data.get('lunch', 'Default Lunch')
47
+ dinner = user_data.get('dinner', 'Default Dinner')
48
+ snacks = user_data.get('snacks', 'Default Snacks')
49
+ exercise_name = user_data.get('exercise_name', 'Default Exercise')
50
+ workout_duration = user_data.get('workout_duration', 30)
51
+ max_pullups = user_data.get('maxPullups')
52
+ min_pullups = user_data.get('minPullups')
53
+ # Prepare the prompt for diet plan generation
54
+ diet_plan_prompt = f"""
55
+ Based on the user's health and personalization details, generate a personalized diet plan in the following format:
56
+
57
+ The user details are as follows:
58
+ - Age: {age}
59
+ - Gender: {gender}
60
+ - Weight: {weight} kg
61
+ - Height: {height} cm
62
+ - Activity Level: {activity_level}
63
+ - Goal: {goal}
64
+ - Known Health Conditions: {health_conditions}
65
+ - Gut Problems: {gut_problems}
66
+ - Dietary Preferences: {dietary_preferences}
67
+ - Food Restrictions/Allergies: {food_restrictions}
68
+ - Location Preferences: {location_preferences}
69
+ - Current Eating Habits: Breakfast: {breakfast}, Lunch: {lunch}, Dinner: {dinner}, Snacks: {snacks}
70
+ - Workout Details: The user performs {exercise_name} for {workout_duration} minutes daily.
71
+
72
+ Return the diet plan only in the following format, no extra text:
73
+ {{
74
+ "Breakfast": [
75
+ {{
76
+ "description": "3 scrambled eggs with spinach and a slice of whole-wheat toast topped with avocado",
77
+ "time": "7:00 AM"
78
+ }}
79
+ ],
80
+ "Lunch": [
81
+ {{
82
+ "description": "Grilled chicken breast with quinoa, steamed broccoli, and a side of hummus",
83
+ "time": "12:30 PM"
84
+ }}
85
+ ],
86
+ "Snacks": [
87
+ {{
88
+ "description": "Greek yogurt with mixed berries and a handful of almonds",
89
+ "time": "4:00 PM"
90
+ }}
91
+ ],
92
+ "Dinner": [
93
+ {{
94
+ "description": "Baked salmon with roasted sweet potatoes and sautéed asparagus",
95
+ "time": "7:30 PM"
96
+ }}
97
+ ],
98
+ "dietSuggestions": [
99
+ {{
100
+ "tip": "Prioritize protein intake to support muscle growth. Aim for 1.2-1.6 grams of protein per kilogram of body weight daily."
101
+ }},
102
+ {{
103
+ "tip": "Include complex carbohydrates like brown rice, quinoa, and sweet potatoes for sustained energy."
104
+ }},
105
+ {{
106
+ "tip": "Don't forget healthy fats from sources like avocado, nuts, seeds, and olive oil."
107
+ }}
108
+ ]
109
+ }}
110
+ """
111
+
112
+ # Create the conversation messages with the prompt
113
+ messages = [
114
+ SystemMessage(content="Generate a personalized diet plan in the specified format."),
115
+ HumanMessage(content=diet_plan_prompt)
116
+ ]
117
+
118
+ # Invoke the model with the prompt
119
+ try:
120
+ result = model.invoke(messages)
121
+
122
+ # Parse the result
123
+ parser = StrOutputParser()
124
+ parsed_response = parser.invoke(result)
125
+
126
+ # Ensure the response is valid JSON
127
+ print(parsed_response)
128
+ diet_plan_json = json.loads(parsed_response)
129
+
130
+ # Save the JSON response to a file
131
+ file_path = "static\diet.json"
132
+ with open(file_path, "w") as json_file:
133
+ json.dump(diet_plan_json, json_file, indent=4)
134
+
135
+ # Display success message and provide download info
136
+ print(f"Diet plan successfully generated and saved to {file_path}.")
137
+ print(f"Download link: {os.path.abspath(file_path)}")
138
+
139
+ except json.JSONDecodeError:
140
+ print("The response could not be parsed as JSON. Please check the output format.")
141
+ except Exception as e:
142
+ print(f"An error occurred: {e}")
143
+
144
+ if __name__ == "__main__":
145
+ predict_diet_plan()
feed_back_llm.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import random
3
+
4
+
5
+ # Function to read the JSON from the file
6
+ def read_json(file_path):
7
+ with open(file_path, 'r') as json_file:
8
+ return json.load(json_file)
9
+
10
+ # Function to add a new day's progress
11
+ def add_next_day_progress(data, next_day_progress):
12
+ # Extract the current day count (last day in the list)
13
+ last_day_number = int(data["daily_progress"][-1]["day"].split(" ")[1]) if data["daily_progress"] else 0
14
+
15
+ # Construct the new day's entry
16
+ next_day_number = last_day_number + 1
17
+ next_day_name = f"Day {next_day_number}"
18
+
19
+
20
+ # Add the next day's data with the LOINC-like identifier
21
+ new_day_entry = { # Unique identifier for the day
22
+ "day": next_day_name,
23
+ "progress": next_day_progress["progress"],
24
+ "rest_time": next_day_progress["rest_time"],
25
+ "workout_time": next_day_progress["workout_time"]
26
+ }
27
+
28
+ data["daily_progress"].append(new_day_entry)
29
+
30
+ # Update weekly summary (rest_time and workout_time)
31
+ total_rest_time = sum(day["rest_time"] for day in data["daily_progress"])
32
+ total_workout_time = sum(day["workout_time"] for day in data["daily_progress"])
33
+
34
+ data["weekly_summary"]["rest_time"] = total_rest_time
35
+ data["weekly_summary"]["workout_time"] = total_workout_time
36
+
37
+ return data
38
+
39
+ # Function to save the updated data to a file
40
+ def save_json(data, file_path):
41
+ with open(file_path, 'w') as json_file:
42
+ json.dump(data, json_file, indent=2)
43
+
44
+
45
+ def update_feed_back():
46
+ # Path to the JSON file
47
+ file_path = r'static\feedback.json'
48
+
49
+ # Step 1: Read the existing JSON data
50
+ data = read_json(file_path)
51
+
52
+ # Example new day data to add (Day 8)
53
+
54
+
55
+ # Generating random values for progress, rest_time, and workout_time
56
+ next_day_progress = {
57
+ "progress": random.randint(50, 100), # Random progress between 50 and 100
58
+ "rest_time": random.randint(4, 10), # Random rest time between 4 and 10 hours
59
+ "workout_time": random.randint(1, 5) # Random workout time between 1 and 5 hours
60
+ }
61
+
62
+ # Step 2: Add the new day to the data and update the weekly summary
63
+ updated_data = add_next_day_progress(data, next_day_progress)
64
+
65
+ # Step 3: Save the updated data back to the file
66
+ save_json(updated_data, file_path)
67
+
68
+ print(f"Updated data saved to {file_path}")
main.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from flask import Flask, render_template, request, Response
2
+ # import cv2
3
+ # import mediapipe as mp
4
+ # import numpy as np
5
+ # import os
6
+
7
+ # app = Flask(_name_)
8
+
9
+ # # Initialize Mediapipe Pose
10
+ # mp_pose = mp.solutions.pose
11
+ # mp_drawing = mp.solutions.drawing_utils
12
+
13
+ # # Function to calculate the angle between three points
14
+ # def calculate_angle(a, b, c):
15
+ # a = np.array(a) # First point
16
+ # b = np.array(b) # Middle point
17
+ # c = np.array(c) # Last point
18
+
19
+ # radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
20
+ # angle = np.abs(radians * 180.0 / np.pi)
21
+
22
+ # if angle > 180.0:
23
+ # angle = 360 - angle
24
+
25
+ # return angle
26
+
27
+ # # Function to check shoulder press posture
28
+ def is_shoulder_press_correct(landmarks, mp_pose):
29
+ # Get coordinates of shoulder, elbow, and wrist (left arm as example)
30
+ shoulder = [landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x,
31
+ landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y]
32
+ elbow = [landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x,
33
+ landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y]
34
+ wrist = [landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].x,
35
+ landmarks[mp_pose.PoseLandmark.LEFT_WRIST.value].y]
36
+
37
+ # Calculate angle at the elbow (shoulder, elbow, wrist)
38
+ elbow_angle = calculate_angle(shoulder, elbow, wrist)
39
+
40
+ # Check if the motion is vertical (wrist higher than elbow)
41
+ if wrist[1] < elbow[1] and elbow[1] < shoulder[1]:
42
+ # Ensure proper angle range for a shoulder press
43
+ if 160 <= elbow_angle <= 180:
44
+ return "Shoulder Press: Correct", True
45
+ else:
46
+ return "Shoulder Press: Incorrect - Elbow angle", False
47
+ else:
48
+ return "Shoulder Press: Incorrect - Alignment", False
49
+
50
+ # Video processing function
51
+ def process_video(video_path):
52
+ cap = cv2.VideoCapture(video_path)
53
+ count = 0
54
+ rep_started = False
55
+
56
+ with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:
57
+ while cap.isOpened():
58
+ ret, frame = cap.read()
59
+ if not ret:
60
+ break
61
+
62
+ # Convert the frame to RGB
63
+ image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
64
+ image.flags.writeable = False
65
+
66
+ # Process the image for pose detection
67
+ results = pose.process(image)
68
+
69
+ # Convert back to BGR for rendering
70
+ image.flags.writeable = True
71
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
72
+
73
+ # Extract landmarks
74
+ if results.pose_landmarks:
75
+ landmarks = results.pose_landmarks.landmark
76
+
77
+ # Check shoulder press posture
78
+ feedback, is_correct = is_shoulder_press_correct(landmarks, mp_pose)
79
+
80
+ # Count reps
81
+ if is_correct and not rep_started:
82
+ rep_started = True
83
+ elif not is_correct and rep_started:
84
+ rep_started = False
85
+ count += 1
86
+
87
+ # Display feedback
88
+ cv2.putText(image, feedback, (50, 50),
89
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0) if is_correct else (0, 0, 255), 2, cv2.LINE_AA)
90
+
91
+ # Display rep count
92
+ cv2.putText(image, f'Reps: {count}', (50, 100),
93
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
94
+
95
+ # Draw landmarks
96
+ mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
97
+
98
+ else:
99
+ # Warn if no landmarks are detected
100
+ cv2.putText(image, "No body detected", (50, 50),
101
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
102
+
103
+ # Encode frame for streaming
104
+ ret, buffer = cv2.imencode('.jpg', image)
105
+ frame = buffer.tobytes()
106
+ yield (b'--frame\r\n'
107
+ b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
108
+
109
+ cap.release()
110
+
111
+ # @app.route('/')
112
+ # def index():
113
+ # return render_template('index.html')
114
+
115
+ # @app.route('/upload', methods=['POST'])
116
+ # def upload():
117
+ # if 'video' not in request.files:
118
+ # return "No video file uploaded", 400
119
+
120
+ # video = request.files['video']
121
+ # video_path = os.path.join('uploads', video.filename)
122
+ # video.save(video_path)
123
+
124
+ # return Response(process_video(video_path), mimetype='multipart/x-mixed-replace; boundary=frame')
125
+
126
+ # if _name_ == "_main_":
127
+ # app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ opencv-python-headless
2
+ mediapipe
3
+ numpy
4
+ Pillow
5
+ firebase-admin
6
+ gunicorn
7
+ Flask
serviceAccountKey.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "service_account",
3
+ "project_id": "fitnessdb-c9b11",
4
+ "private_key_id": "d304ad2536ee8ad34e6805b2e3ce6b9b8f2a0fcc",
5
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNAG4aKS454RQl\ne35uIQYR8BA67RzgRng5DqGM+3E+8vvVUb2LZVzxIFlYP2P6D/U1CgbB8IdfsMx2\nDEz5CnaRsPvz5TvxSNxrqRM6mGRPvg1+HA1auN11LUIiMoWkhNtYXUAG3Ae6TjED\nppykPxlTHxbcnkvZGSQn4DtzHU8xNoLe/lHABHWNMz8kbWSwaFK0Nh+azsxCePzU\nYKgE29OcJDqz3CXocxn9XrTVNzlYCVNcdpWVdjBRRz9Pi4qMZ+APuc32eMcy6E4z\n7XmSTxM4/G/wXei6Kref35Vn+PpBypRgIzmOPvx+vTQhMlAMMlatRgl1XhM3A5ES\n67Sg8aW1AgMBAAECggEAVNSiOxoiYloU+6O8QDdTKzYTiUbYZahTlIzM5imghaOH\n+ZCfJmFWEgPIZP+qT+6tkfqprDRr2HmxSgIyEfY19Xju8dDAuspjR/vJlLw9+k+T\nhsV18z4/if8l+D++1MMTf1/rIEuJuRslJjUaac8gnChnzfiFO3uvXf7oquyMejit\nnL9ss04Fw6TPIv8P8IpERjdAroey9kfKtRXZ167QN7xw87dLp07h0D3T5Z2juVMg\nrA00c8Fv6zer26Pt+wdPTnMc0AYoBjPxg2yBUtajcWbdcZNjswzeH0w5rtSQ8bsJ\nTRF1qOXdJL4JUe1fk3LyXz7viGfDMlrg/roWujMcSwKBgQD6URATlqnZc15zUDfQ\n/YDyTlhhnXkeMmAOdR1ZX916J6+4XRb2GGwuim5gCpNJSva4UyChujxZZKuWXkke\n5jvu4K/DeFuSW0rrldb9GMCbIov0wmqpoeC7wSUupnJ3feaIkT3DeqC1cQYb0Nl1\n1eL3SGAAM/dGhOZR9/0hTPSOqwKBgQDRp/qpRsGqxMT2kuFEUJBTS7fLpqCfLm8n\nS3Bh6R5QLg/PMNgLNsJL8fZ62SdQ9s9/sVNq//r/cCA3WzQHTjNIdfXhSXu6/Iik\n54TkAZjedfnHQ5jUlSNLKqwGYEZ4Ih9wwCr97yXTma0M8HnDjccyemxEHkTYEcol\nP7VOJYEdHwKBgAoWVDCF5MhXhtncxLMOVDDviU49u1DFNOvAOnOMkm9GxCUI01EN\ngOaLO5FxO6g7dh/NccYyrBXqIaQInqe5HXct5MdaxU3rkeRWgHhok/JsfPlbEFNP\nq6/FQ8tSd9Bq6WxddgC3o1xMdrOOQgUmnmParcu0TGWyG1n4RWIfKMfLAoGALS1P\nPC69CLlB4AgidoANuYU1Y7LSJbrxxLviyZZcK9bhHTpfM3tnPsoy3KHycOXeLJvf\nZ80lHungZ01F1tUpA9I3W4ZkHRTRtQcWgbM+Z6FwY1nTkutYIZheXTldtgFUWQ1v\ntixUMFaLDaC7/EGOzPfIYJ1NJGog7wndXauDOO0CgYEAmhbjxZVh9tRFoQJVSt/7\nEklUQXv3MHJQGsM9tglTJBGS5JcYD1vYpNxCFYt3cl5WmM1Gr7OWHre38JCGBgMv\n6+7uGjD5LPHbiMLnSnC1aPl8Tfw8p1hh92rxS6MN4A1Xq72hv73/o9P1nLl5mP9Z\ng/nz1ISSsQi2EMEtZIQIqtc=\n-----END PRIVATE KEY-----\n",
6
+ "client_email": "firebase-adminsdk-lpyql@fitnessdb-c9b11.iam.gserviceaccount.com",
7
+ "client_id": "110696125764328036804",
8
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+ "token_uri": "https://oauth2.googleapis.com/token",
10
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-lpyql%40fitnessdb-c9b11.iam.gserviceaccount.com",
12
+ "universe_domain": "googleapis.com"
13
+ }
14
+