Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import os
|
4 |
+
import cv2
|
5 |
+
import mediapipe as mp
|
6 |
+
from datetime import datetime, timedelta
|
7 |
+
import random
|
8 |
+
import firebase_admin
|
9 |
+
from firebase_admin import credentials, firestore
|
10 |
+
|
11 |
+
# Firebase Initialization
|
12 |
+
if not firebase_admin._apps:
|
13 |
+
try:
|
14 |
+
cred = credentials.Certificate("growkids-f8bfe-firebase-adminsdk-koo88-8a3a33da6d") # Replace with your Firebase credentials JSON file
|
15 |
+
firebase_admin.initialize_app(cred)
|
16 |
+
db = firestore.client()
|
17 |
+
print("Firebase initialized successfully!")
|
18 |
+
except Exception as e:
|
19 |
+
print(f"Error initializing Firebase: {e}")
|
20 |
+
raise e # Reraise to stop further execution if Firebase setup fails
|
21 |
+
|
22 |
+
# MediaPipe Pose Detection Setup
|
23 |
+
mp_pose = mp.solutions.pose
|
24 |
+
pose = mp_pose.Pose(static_image_mode=False, model_complexity=1, enable_segmentation=True)
|
25 |
+
mp_drawing = mp.solutions.drawing_utils
|
26 |
+
|
27 |
+
def analyze_pose(proof_file, task):
|
28 |
+
"""Analyze uploaded image/video to detect pose and validate the task."""
|
29 |
+
try:
|
30 |
+
if proof_file.name.endswith(('.jpg', '.jpeg', '.png')): # Image proof
|
31 |
+
image = cv2.imread(proof_file.name)
|
32 |
+
if image is None:
|
33 |
+
return "Invalid image file. Please retry."
|
34 |
+
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
35 |
+
results = pose.process(image_rgb)
|
36 |
+
if results.pose_landmarks:
|
37 |
+
return f"Pose detected successfully for '{task}'! Proof is valid."
|
38 |
+
else:
|
39 |
+
return f"No pose detected in the uploaded image for '{task}'. Please retry."
|
40 |
+
elif proof_file.name.endswith(('.mp4', '.avi', '.mov')): # Video proof
|
41 |
+
cap = cv2.VideoCapture(proof_file.name)
|
42 |
+
pose_detected = False
|
43 |
+
while cap.isOpened():
|
44 |
+
ret, frame = cap.read()
|
45 |
+
if not ret:
|
46 |
+
break
|
47 |
+
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
48 |
+
results = pose.process(image_rgb)
|
49 |
+
if results.pose_landmarks:
|
50 |
+
pose_detected = True
|
51 |
+
break
|
52 |
+
cap.release()
|
53 |
+
return (
|
54 |
+
f"Pose detected successfully in video for '{task}'! Proof is valid."
|
55 |
+
if pose_detected
|
56 |
+
else f"No pose detected in video for '{task}'. Please retry."
|
57 |
+
)
|
58 |
+
else:
|
59 |
+
return "Invalid file format. Please upload an image or video."
|
60 |
+
except Exception as e:
|
61 |
+
print(f"Error in analyze_pose: {e}")
|
62 |
+
return f"Error analyzing the file: {e}"
|
63 |
+
|
64 |
+
def submit_proof(username, task, proof):
|
65 |
+
"""Submit proof of task completion and validate using pose analysis."""
|
66 |
+
try:
|
67 |
+
if not proof:
|
68 |
+
return "Please upload a valid proof file (Image/Video)."
|
69 |
+
|
70 |
+
# Validate if the task exists for the username in Firebase
|
71 |
+
user_doc = db.collection("users").document(username.lower()).get()
|
72 |
+
if not user_doc.exists:
|
73 |
+
return f"User '{username}' not found. Please check the username."
|
74 |
+
|
75 |
+
user_data = user_doc.to_dict()
|
76 |
+
user_tasks = user_data.get("tasks", [])
|
77 |
+
task_names = [t["task"] for t in user_tasks]
|
78 |
+
if task not in task_names:
|
79 |
+
return f"Task '{task}' not found for the user '{username}'. Please check the task name."
|
80 |
+
|
81 |
+
# Check if proof has already been uploaded for the task
|
82 |
+
for t in user_tasks:
|
83 |
+
if t["task"] == task and t.get("proof_uploaded"):
|
84 |
+
return f"Proof already uploaded for task '{task}'. Please upload a new proof if necessary."
|
85 |
+
|
86 |
+
# Analyze the proof file
|
87 |
+
analysis_result = analyze_pose(proof, task)
|
88 |
+
if "successfully" not in analysis_result:
|
89 |
+
return analysis_result
|
90 |
+
|
91 |
+
# Log proof submission in Firebase (without assigning reward)
|
92 |
+
try:
|
93 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
94 |
+
# Update the task status to 'completed' but do not assign a reward here
|
95 |
+
for t in user_tasks:
|
96 |
+
if t["task"] == task:
|
97 |
+
t["status"] = "completed"
|
98 |
+
t["timestamp"] = timestamp
|
99 |
+
t["proof_uploaded"] = True # Mark that proof has been uploaded
|
100 |
+
|
101 |
+
# Save updated task status in Firebase
|
102 |
+
db.collection("users").document(username.lower()).set({"tasks": user_tasks}, merge=True)
|
103 |
+
|
104 |
+
print(f"Proof for task '{task}' submitted successfully for {username}!")
|
105 |
+
return f"Task '{task}' completed successfully! Please assign a reward in the 'Progress and Rewards Tracker' tab."
|
106 |
+
except Exception as e:
|
107 |
+
print(f"Error logging proof submission in Firebase: {e}")
|
108 |
+
return f"Error submitting proof to Firebase: {e}"
|
109 |
+
except Exception as e:
|
110 |
+
print(f"Error submitting proof: {e}")
|
111 |
+
return f"Error: {e}"
|
112 |
+
|
113 |
+
def generate_fitness_tasks(username, age, fitness_level):
|
114 |
+
"""Generate a single fitness task based on user's age and fitness level."""
|
115 |
+
try:
|
116 |
+
age = int(age) # Convert age to integer
|
117 |
+
if not (7 <= age <= 14):
|
118 |
+
return "Age must be between 7 and 14.", "" # Early return on invalid age
|
119 |
+
|
120 |
+
if fitness_level not in ["beginner", "intermediate", "advanced"]:
|
121 |
+
return "Please select a valid fitness level.", "" # Early return on invalid fitness level
|
122 |
+
|
123 |
+
# Task definitions based on age and fitness level
|
124 |
+
tasks = {
|
125 |
+
(7, 10): {
|
126 |
+
"beginner": {"task": "Jumping Jacks", "details": "10 reps"},
|
127 |
+
"intermediate": {"task": "Push-ups", "details": "10 reps"},
|
128 |
+
"advanced": {"task": "Squats", "details": "10 reps"},
|
129 |
+
},
|
130 |
+
(11, 12): {
|
131 |
+
"beginner": {"task": "Jumping Jacks", "details": "15 reps"},
|
132 |
+
"intermediate": {"task": "Push-ups", "details": "15 reps"},
|
133 |
+
"advanced": {"task": "Squats", "details": "15 reps"},
|
134 |
+
},
|
135 |
+
(13, 14): {
|
136 |
+
"beginner": {"task": "Jumping Jacks", "details": "20 reps"},
|
137 |
+
"intermediate": {"task": "Push-ups", "details": "20 reps"},
|
138 |
+
"advanced": {"task": "Squats", "details": "20 reps"},
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
# Determine the task set based on age and fitness level
|
143 |
+
for age_range, level_tasks in tasks.items():
|
144 |
+
if age_range[0] <= age <= age_range[1]:
|
145 |
+
selected_task = level_tasks[fitness_level]
|
146 |
+
break
|
147 |
+
else:
|
148 |
+
return "Invalid age range. Tasks not available.", ""
|
149 |
+
|
150 |
+
# Save the task in Firebase for the user
|
151 |
+
user_doc = db.collection("users").document(username.lower()).get()
|
152 |
+
if not user_doc.exists:
|
153 |
+
return f"User '{username}' not found. Please sign up first.", ""
|
154 |
+
|
155 |
+
task_list = [{"task": selected_task["task"], "details": selected_task["details"], "status": "pending", "proof_uploaded": False}]
|
156 |
+
db.collection("users").document(username.lower()).set({"tasks": task_list}, merge=True)
|
157 |
+
|
158 |
+
# Return task and a message
|
159 |
+
task_display = f"{selected_task['task']} - {selected_task['details']}"
|
160 |
+
return f"Task generated successfully for {username}.", task_display
|
161 |
+
except Exception as e:
|
162 |
+
print(f"Error in generate_fitness_tasks: {e}")
|
163 |
+
return f"Error generating tasks: {e}", ""
|
164 |
+
|
165 |
+
def view_activity_log(username):
|
166 |
+
"""Retrieve and display user's activity log from Firebase."""
|
167 |
+
try:
|
168 |
+
# Fetch user data from Firebase
|
169 |
+
user_doc = db.collection("users").document(username.lower()).get()
|
170 |
+
if not user_doc.exists:
|
171 |
+
return f"User '{username}' not found."
|
172 |
+
|
173 |
+
user_data = user_doc.to_dict()
|
174 |
+
user_tasks = user_data.get("tasks", [])
|
175 |
+
|
176 |
+
# Convert task data into a pandas DataFrame for display
|
177 |
+
tasks_data = [
|
178 |
+
{
|
179 |
+
"Task": task["task"],
|
180 |
+
"Details": task["details"],
|
181 |
+
"Status": task["status"],
|
182 |
+
"Timestamp": task.get("timestamp", "Not available"),
|
183 |
+
"Proof Uploaded": task.get("proof_uploaded", False),
|
184 |
+
"Reward": task.get("reward", "Not assigned")
|
185 |
+
}
|
186 |
+
for task in user_tasks
|
187 |
+
]
|
188 |
+
|
189 |
+
df = pd.DataFrame(tasks_data)
|
190 |
+
return df
|
191 |
+
except Exception as e:
|
192 |
+
print(f"Error fetching activity log: {e}")
|
193 |
+
return f"Error fetching activity log: {e}"
|
194 |
+
|
195 |
+
def assign_reward(username, task, reward):
|
196 |
+
"""Assign reward to a task for a specific user."""
|
197 |
+
try:
|
198 |
+
# Fetch user data from Firebase
|
199 |
+
user_doc = db.collection("users").document(username.lower()).get()
|
200 |
+
if not user_doc.exists:
|
201 |
+
return f"User '{username}' not found."
|
202 |
+
|
203 |
+
user_data = user_doc.to_dict()
|
204 |
+
user_tasks = user_data.get("tasks", [])
|
205 |
+
|
206 |
+
# Check if the task exists and is completed
|
207 |
+
for t in user_tasks:
|
208 |
+
if t["task"] == task and t.get("status") == "completed":
|
209 |
+
t["reward"] = reward # Assign the reward
|
210 |
+
db.collection("users").document(username.lower()).set({"tasks": user_tasks}, merge=True)
|
211 |
+
return f"Reward '{reward}' assigned to task '{task}' for user '{username}'."
|
212 |
+
|
213 |
+
return f"Task '{task}' not completed or not found for user '{username}'."
|
214 |
+
|
215 |
+
except Exception as e:
|
216 |
+
print(f"Error assigning reward: {e}")
|
217 |
+
return f"Error assigning reward: {e}"
|
218 |
+
|
219 |
+
def create_app():
|
220 |
+
with gr.Blocks() as app:
|
221 |
+
gr.Markdown("## Fitness Challenge App with Firebase Integration")
|
222 |
+
|
223 |
+
# Sign Up / Login
|
224 |
+
with gr.Tab("Sign Up / Login"):
|
225 |
+
username_input = gr.Textbox(label="Enter Username")
|
226 |
+
password_input = gr.Textbox(label="Enter Password", type="password") # Password field
|
227 |
+
sign_up_login_btn = gr.Button("Sign Up / Login")
|
228 |
+
sign_up_login_status = gr.Textbox(label="Status")
|
229 |
+
|
230 |
+
def handle_sign_up_login(username, password):
|
231 |
+
try:
|
232 |
+
user_doc = db.collection("users").document(username.lower()).get()
|
233 |
+
if user_doc.exists:
|
234 |
+
stored_password = user_doc.get("password")
|
235 |
+
if stored_password == password:
|
236 |
+
return f"Welcome back, {username}!", None # If password matches, login success
|
237 |
+
else:
|
238 |
+
return "Incorrect password. Please try again.", None
|
239 |
+
else:
|
240 |
+
# If user does not exist, sign up
|
241 |
+
db.collection("users").document(username.lower()).set({"username": username.lower(), "password": password}, merge=True)
|
242 |
+
return f"Sign-up successful! Welcome, {username}!", None
|
243 |
+
except Exception as e:
|
244 |
+
return f"Error: {e}", None
|
245 |
+
|
246 |
+
sign_up_login_btn.click(handle_sign_up_login, [username_input, password_input], sign_up_login_status)
|
247 |
+
|
248 |
+
# Generate Tasks
|
249 |
+
with gr.Tab("Generate Tasks"):
|
250 |
+
username_gen = gr.Textbox(label="Enter Username")
|
251 |
+
age = gr.Textbox(label="Enter Age (7-14)", placeholder="E.g., 10")
|
252 |
+
fitness_level = gr.Radio(["beginner", "intermediate", "advanced"], label="Fitness Level")
|
253 |
+
generate_btn = gr.Button("Generate Tasks")
|
254 |
+
task_output = gr.Textbox(label="Task Status")
|
255 |
+
task_display = gr.Textbox(label="Generated Task", interactive=False)
|
256 |
+
|
257 |
+
generate_btn.click(generate_fitness_tasks, [username_gen, age, fitness_level], [task_output, task_display])
|
258 |
+
|
259 |
+
# Submit Proof
|
260 |
+
with gr.Tab("Submit Proof"):
|
261 |
+
username_proof = gr.Textbox(label="Enter Username")
|
262 |
+
task_name = gr.Textbox(label="Enter Task Name")
|
263 |
+
proof_upload = gr.File(label="Upload Proof (Image/Video)")
|
264 |
+
submit_btn = gr.Button("Submit Proof")
|
265 |
+
submission_status = gr.Textbox(label="Submission Status")
|
266 |
+
|
267 |
+
submit_btn.click(submit_proof, [username_proof, task_name, proof_upload], submission_status)
|
268 |
+
|
269 |
+
# Progress and Rewards Tracker
|
270 |
+
with gr.Tab("Progress and Rewards Tracker"):
|
271 |
+
username_log = gr.Textbox(label="Enter Username")
|
272 |
+
log_output = gr.DataFrame(label="Progress and Rewards")
|
273 |
+
log_btn = gr.Button("View Tracker")
|
274 |
+
reward_selector = gr.Radio(["Toy", "Little Buck", "Chocolate"], label="Select Reward")
|
275 |
+
task_to_reward = gr.Textbox(label="Task Name to Assign Reward")
|
276 |
+
assign_btn = gr.Button("Assign Reward")
|
277 |
+
reward_status = gr.Textbox(label="Reward Assignment Status")
|
278 |
+
|
279 |
+
log_btn.click(view_activity_log, [username_log], log_output)
|
280 |
+
assign_btn.click(assign_reward, [username_log, task_to_reward, reward_selector], reward_status)
|
281 |
+
|
282 |
+
return app
|
283 |
+
|
284 |
+
app = create_app()
|
285 |
+
app.launch()
|