Skip to content

Commit 54a21a0

Browse files
authored
App (#121)
1 parent 6a49ccb commit 54a21a0

26 files changed

Lines changed: 4105 additions & 823 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,4 @@ shoot.jpg
185185
node_modules
186186
.mongodb
187187
vendor
188+
app.log.*

copy-me_backend/package-lock.json

Lines changed: 3015 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

copy-me_backend/src/controllers/processedData.controller.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,32 @@ export const uploadProcessedData = async (req: AuthenticatedRequest, res: Respon
216216
formData.append('exerciseId', exercise_id);
217217
formData.append('fileType', fileType);
218218

219-
await fetch(`${process.env.AI_API_URL}/process`, {
219+
const response = await fetch(`${process.env.AI_API_URL}/process`, {
220220
method: 'POST',
221221
headers: {
222222
Authorization: `Bearer ${process.env.AI_API_KEY}`,
223223
},
224224
body: formData,
225225
});
226226

227+
const responseData = await response.json();
228+
console.log('Response from AI API:', responseData.frames.length);
229+
ProcessedData.updateOne(
230+
{ _id: processedData.id },
231+
{
232+
$set: {
233+
frames: responseData.frames || [],
234+
},
235+
},
236+
).catch((error) => {
237+
logger.error('Erreur lors de la mise à jour de la processed data:', error);
238+
return res.status(500).json({
239+
success: false,
240+
message: 'Erreur lors de la mise à jour de la processed data',
241+
error: error instanceof Error ? error.message : 'Erreur inconnue',
242+
});
243+
});
244+
227245
return res.status(201).json({
228246
success: true,
229247
data: processedData,
@@ -237,3 +255,44 @@ export const uploadProcessedData = async (req: AuthenticatedRequest, res: Respon
237255
});
238256
}
239257
};
258+
259+
export const analyzeProcessedData = async (req: AuthenticatedRequest, res: Response) => {
260+
try {
261+
console.log('Analyse des données traitées:', req.body);
262+
const response = await fetch(`${process.env.AI_API_URL}/analyze`, {
263+
method: 'POST',
264+
headers: {
265+
Authorization: `Bearer ${process.env.AI_API_KEY}`,
266+
'Content-Type': 'application/json',
267+
},
268+
body: JSON.stringify({
269+
email: req.body.email,
270+
video_id: req.body.video_id,
271+
reference_id: req.body.reference_id,
272+
}),
273+
});
274+
275+
if (!response.ok) {
276+
const errorText = await response.text();
277+
logger.error('Erreur lors de l\'analyse des données traitées:', errorText);
278+
return res.status(response.status).json({
279+
success: false,
280+
message: 'Erreur lors de l\'analyse des données traitées',
281+
error: errorText,
282+
});
283+
}
284+
const responseData = await response.json();
285+
286+
return res.status(201).json({
287+
success: true,
288+
data: responseData,
289+
});
290+
} catch (error) {
291+
logger.error("Erreur lors de l'analyse des données traitées:", error);
292+
return res.status(500).json({
293+
success: false,
294+
message: "Erreur lors de l'analyse des données traitées",
295+
error: error instanceof Error ? error.message : 'Erreur inconnue',
296+
});
297+
}
298+
};

copy-me_backend/src/routes/processedData.routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
getProcessedDataById,
77
uploadProcessedData,
88
upload,
9+
analyzeProcessedData,
910
} from '../controllers/processedData.controller';
1011
import { authenticateToken } from '../middlewares/auth.middleware';
1112
const router = Router();
@@ -20,6 +21,10 @@ router
2021
uploadProcessedData(req, res);
2122
});
2223

24+
router.route('/analyze').post(authenticateToken, (req, res) => {
25+
analyzeProcessedData(req, res);
26+
});
27+
2328
router.route('/:id').get(authenticateToken, (req, res) => {
2429
getProcessedDataById(req, res);
2530
});

copyme-ai/api/v1/model.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ class ProcessRequest(BaseModel):
3434
processedDataId: str = Field(..., examples=["680a1e190ceb2230eeb132b6"])
3535
allow_training: Optional[bool] = False
3636

37+
def clean(obj):
38+
if isinstance(obj, float):
39+
return 0.0 if (math.isnan(obj) or math.isinf(obj)) else obj
40+
elif isinstance(obj, dict):
41+
return {k: clean(v) for k, v in obj.items()}
42+
elif isinstance(obj, list):
43+
return [clean(v) for v in obj]
44+
elif hasattr(obj, "model_dump"):
45+
return clean(obj.model_dump())
46+
return obj
47+
48+
3749
@router.post("/process", response_model=ProcessResponse)
3850
async def process(
3951
request: Request,

copyme-ai/yolov8_basketball/mediapipe.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ def get_keypoints(self, image):
1010
results = self.pose.process(image)
1111
if not hasattr(results, 'pose_landmarks') or results.pose_landmarks is None:
1212
return None
13+
1314
landmarks = results.pose_landmarks.landmark
1415
keypoints = {}
16+
1517
for idx in [19, 20, 31, 32]:
1618
if idx < len(landmarks):
1719
lm = landmarks[idx]
18-
if lm.visibility > 0.5:
20+
if hasattr(lm, "visibility") and lm.visibility > 0.5:
1921
keypoints[idx] = (lm.x, lm.y, lm.z)
20-
return keypoints if keypoints else None
22+
23+
if not keypoints:
24+
print("[INFO] Aucun point suffisamment visible")
25+
return None
26+
27+
return keypoints

copymeapp/App.tsx

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useCallback } from 'react';
22
import { NavigationContainer } from '@react-navigation/native';
33
import { createStackNavigator } from '@react-navigation/stack';
44
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
@@ -25,6 +25,18 @@ import color from './app/theme/color';
2525
const Stack = createStackNavigator();
2626
const Tab = createBottomTabNavigator();
2727

28+
const HomeIcon = ({ color: iconColor, size }: { color: string; size: number }) =>
29+
<Home color={iconColor} size={size} />;
30+
31+
const DumbbellIcon = ({ color: iconColor, size }: { color: string; size: number }) =>
32+
<Dumbbell color={iconColor} size={size} />;
33+
34+
const BarChartIcon = ({ color: iconColor, size }: { color: string; size: number }) =>
35+
<BarChart3 color={iconColor} size={size} />;
36+
37+
const UserIcon = ({ color: iconColor, size }: { color: string; size: number }) =>
38+
<User color={iconColor} size={size} />;
39+
2840
function TabNavigator() {
2941
return (
3042
<Tab.Navigator
@@ -44,39 +56,31 @@ function TabNavigator() {
4456
component={HomeScreen}
4557
options={{
4658
tabBarLabel: 'Accueil',
47-
tabBarIcon: ({ color, size }) => (
48-
<Home color={color} size={size} />
49-
),
59+
tabBarIcon: HomeIcon,
5060
}}
5161
/>
5262
<Tab.Screen
5363
name="Exercises"
5464
component={ExercisesScreen}
5565
options={{
5666
tabBarLabel: 'Exercices',
57-
tabBarIcon: ({ color, size }) => (
58-
<Dumbbell color={color} size={size} />
59-
),
67+
tabBarIcon: DumbbellIcon,
6068
}}
6169
/>
6270
<Tab.Screen
6371
name="Analysis"
6472
component={AnalysisScreen}
6573
options={{
6674
tabBarLabel: 'Analyse',
67-
tabBarIcon: ({ color, size }) => (
68-
<BarChart3 color={color} size={size} />
69-
),
75+
tabBarIcon: BarChartIcon,
7076
}}
7177
/>
7278
<Tab.Screen
7379
name="Profile"
7480
component={ProfileScreen}
7581
options={{
7682
tabBarLabel: 'Profil',
77-
tabBarIcon: ({ color, size }) => (
78-
<User color={color} size={size} />
79-
),
83+
tabBarIcon: UserIcon,
8084
}}
8185
/>
8286
</Tab.Navigator>
@@ -98,10 +102,66 @@ export default function App() {
98102
<Stack.Screen name="Login" component={LoginScreen} />
99103
<Stack.Screen name="Register" component={RegisterScreen} />
100104
<Stack.Screen name="Main" component={TabNavigator} />
101-
<Stack.Screen name="ExerciseDetail" component={ExerciseDetailScreen} />
102-
<Stack.Screen name="ExerciseSession" component={ExerciseSessionScreen} />
103-
<Stack.Screen name="ExerciseResults" component={ExerciseResultsScreen} />
104-
<Stack.Screen name="Analyze" component={AnalyzeScreen} />
105+
<Stack.Screen
106+
name="ExerciseDetail"
107+
component={ExerciseDetailScreen}
108+
options={{
109+
headerShown: true,
110+
title: "Détail de l'exercice",
111+
headerStyle: {
112+
backgroundColor: color.colors.background,
113+
},
114+
headerTintColor: color.colors.textPrimary,
115+
headerTitleStyle: {
116+
fontWeight: 'bold',
117+
},
118+
}}
119+
/>
120+
<Stack.Screen
121+
name="ExerciseSession"
122+
component={ExerciseSessionScreen}
123+
options={{
124+
headerShown: true,
125+
title: "Session d'exercice",
126+
headerStyle: {
127+
backgroundColor: color.colors.background,
128+
},
129+
headerTintColor: color.colors.textPrimary,
130+
headerTitleStyle: {
131+
fontWeight: 'bold',
132+
},
133+
}}
134+
/>
135+
<Stack.Screen
136+
name="ExerciseResults"
137+
component={ExerciseResultsScreen}
138+
options={{
139+
headerShown: true,
140+
title: 'Résultats',
141+
headerStyle: {
142+
backgroundColor: color.colors.background,
143+
},
144+
headerTintColor: color.colors.textPrimary,
145+
headerTitleStyle: {
146+
fontWeight: 'bold',
147+
},
148+
}}
149+
/>
150+
<Stack.Screen
151+
name="Analyze"
152+
component={AnalyzeScreen}
153+
options={{
154+
headerShown: true,
155+
title: 'Analyse',
156+
headerStyle: {
157+
backgroundColor: color.colors.background,
158+
},
159+
headerTintColor: color.colors.textPrimary,
160+
headerTitleStyle: {
161+
fontWeight: 'bold',
162+
},
163+
}}
164+
/>
105165
<Stack.Screen name="NotFound" component={NotFoundScreen} />
106166
</Stack.Navigator>
107167
</AuthProvider>

copymeapp/Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,4 @@ RUBY VERSION
113113
ruby 2.6.10p210
114114

115115
BUNDLED WITH
116-
1.17.2
116+
2.6.9

copymeapp/app/(tabs)/exercises.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import styles from '../styles/exercisesTabs';
1414
import color from '@/app/theme/color';
1515
import { Exercise } from '@/constants/interface';
1616
import { SafeAreaProvider } from 'react-native-safe-area-context';
17+
import { Button } from '@/components/Button';
1718

1819
const levelFilters: FilterOption[] = [
1920
{ id: 'all', label: 'All Levels' },
@@ -32,7 +33,9 @@ export default function ExercisesScreen() {
3233

3334
const getExercises = async () => {
3435
try {
36+
console.log('Fetching exercises from API...');
3537
const response = await api.get('/exercises');
38+
console.log('Fetched exercises:', response);
3639
const data = response as { data: Exercise[] };
3740
setExercisesData(data?.data || []);
3841
setIsLoading(false);
@@ -132,6 +135,7 @@ export default function ExercisesScreen() {
132135
<ThemedText type="default" style={styles.emptyStateText}>
133136
Try adjusting your search or filters
134137
</ThemedText>
138+
<Button title='Reload' onPress={getExercises} loading={isLoading} />
135139
</ThemedView>
136140
)}
137141
</ThemedSafeAreaView>

copymeapp/app/(tabs)/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { useNavigation } from '@react-navigation/native';
1313
import { ProcessedData } from '@/constants/processedData';
1414

1515
export default function HomeScreen() {
16-
const { reviews, loading, error } = useReviews();
16+
const { reviews, loading } = useReviews();
1717
const navigation = useNavigation();
1818

1919
const handleAnalysisPress = (analysis: ProcessedData) => {
@@ -31,15 +31,15 @@ export default function HomeScreen() {
3131
<ThemedView style={styles.container}>
3232
<ThemedView style={styles.titleContainer}>
3333
<ThemedText type='title'>Dashboard</ThemedText>
34-
{/* <HelloWave /> */}
34+
<HelloWave />
3535
</ThemedView>
3636
<Image
3737
source={require('@/assets/images/WelcomeCta2.png')}
3838
style={styles.cta}
3939
/>
4040

4141
<ThemedView style={styles.reviewContainer}>
42-
<SeeAll text='Last reviews' />
42+
<SeeAll text='Last reviews' goTo='Analysis'/>
4343
<ThemedView style={{ flex: 1, gap: 8 }}>
4444
{reviews.length > 0 ? (
4545
loading ? (
@@ -60,7 +60,7 @@ export default function HomeScreen() {
6060
</ThemedView>
6161
</ThemedView>
6262
<ThemedView style={styles.reviewContainer}>
63-
<SeeAll text='Overall Stats' cta='See More' />
63+
<SeeAll text='Overall Stats' cta='See More' goTo=''/>
6464
<OverallStats />
6565
</ThemedView>
6666
</ThemedView>

0 commit comments

Comments
 (0)