Macbook commited on
Commit
f9b1f70
·
1 Parent(s): a21ba47

Add comprehensive exercises with categories, images, and French translations

Browse files

- 4 exercise categories: Fundamentals, Speech Practice, Visual Learning, Sound Imitation
- 59+ exercises with images from Unsplash
- Full French translation support
- New API endpoints for categories and filtered exercises

🤖 Generated with Claude Code

api/data/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Data module
api/data/exercises.py ADDED
@@ -0,0 +1,906 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Comprehensive Speech Therapy Exercises Database
3
+ Categories: Fundamentals, Speech Practice, Visual Learning, Sound Imitation
4
+ Languages: English (en), French (fr)
5
+ """
6
+
7
+ from typing import List, Dict, Optional
8
+ from pydantic import BaseModel
9
+ from enum import Enum
10
+
11
+
12
+ class ExerciseType(str, Enum):
13
+ # Fundamentals
14
+ BREATHING = "breathing"
15
+ ARTICULATION = "articulation"
16
+ PHONEME = "phoneme"
17
+
18
+ # Speech Practice
19
+ WORD_REPETITION = "word_repetition"
20
+ SENTENCE_READING = "sentence_reading"
21
+ TONGUE_TWISTER = "tongue_twister"
22
+
23
+ # Visual Learning
24
+ COLOR = "color"
25
+ OBJECT = "object"
26
+ ANIMAL = "animal"
27
+ ACTION = "action"
28
+
29
+ # Sound Imitation
30
+ ANIMAL_SOUND = "animal_sound"
31
+ ENVIRONMENTAL_SOUND = "environmental_sound"
32
+
33
+
34
+ class Difficulty(str, Enum):
35
+ EASY = "easy"
36
+ MEDIUM = "medium"
37
+ HARD = "hard"
38
+
39
+
40
+ class Category(BaseModel):
41
+ id: str
42
+ name: Dict[str, str] # {"en": "...", "fr": "..."}
43
+ description: Dict[str, str]
44
+ icon: str
45
+ subcategories: List[str]
46
+
47
+
48
+ class Exercise(BaseModel):
49
+ id: str
50
+ type: ExerciseType
51
+ category: str
52
+ subcategory: str
53
+ difficulty: Difficulty
54
+ title: Dict[str, str]
55
+ target_text: Dict[str, str]
56
+ instructions: Dict[str, str]
57
+ image_url: Optional[str] = None
58
+ audio_url: Optional[str] = None
59
+ phoneme_focus: Optional[List[str]] = None
60
+
61
+
62
+ # =============================================================================
63
+ # CATEGORIES
64
+ # =============================================================================
65
+
66
+ CATEGORIES: List[Dict] = [
67
+ {
68
+ "id": "fundamentals",
69
+ "name": {"en": "Fundamentals", "fr": "Fondamentaux"},
70
+ "description": {
71
+ "en": "Build your foundation with breathing and articulation exercises",
72
+ "fr": "Construisez votre base avec des exercices de respiration et d'articulation"
73
+ },
74
+ "icon": "🎯",
75
+ "subcategories": ["breathing", "articulation", "phoneme"]
76
+ },
77
+ {
78
+ "id": "speech_practice",
79
+ "name": {"en": "Speech Practice", "fr": "Pratique de la Parole"},
80
+ "description": {
81
+ "en": "Practice words, sentences, and tongue twisters",
82
+ "fr": "Pratiquez des mots, des phrases et des virelangues"
83
+ },
84
+ "icon": "📖",
85
+ "subcategories": ["word_repetition", "sentence_reading", "tongue_twister"]
86
+ },
87
+ {
88
+ "id": "visual_learning",
89
+ "name": {"en": "Visual Learning", "fr": "Apprentissage Visuel"},
90
+ "description": {
91
+ "en": "Learn by identifying colors, objects, animals, and actions",
92
+ "fr": "Apprenez en identifiant les couleurs, objets, animaux et actions"
93
+ },
94
+ "icon": "🖼️",
95
+ "subcategories": ["color", "object", "animal", "action"]
96
+ },
97
+ {
98
+ "id": "sound_imitation",
99
+ "name": {"en": "Sound Imitation", "fr": "Imitation de Sons"},
100
+ "description": {
101
+ "en": "Imitate animal and environmental sounds",
102
+ "fr": "Imitez les sons d'animaux et de l'environnement"
103
+ },
104
+ "icon": "🔊",
105
+ "subcategories": ["animal_sound", "environmental_sound"]
106
+ }
107
+ ]
108
+
109
+ SUBCATEGORIES: Dict[str, Dict] = {
110
+ "breathing": {
111
+ "name": {"en": "Breathing Exercises", "fr": "Exercices de Respiration"},
112
+ "description": {"en": "Control your breath for better speech", "fr": "Contrôlez votre respiration pour mieux parler"}
113
+ },
114
+ "articulation": {
115
+ "name": {"en": "Articulation Drills", "fr": "Exercices d'Articulation"},
116
+ "description": {"en": "Improve mouth and tongue movements", "fr": "Améliorez les mouvements de la bouche et de la langue"}
117
+ },
118
+ "phoneme": {
119
+ "name": {"en": "Phoneme Practice", "fr": "Pratique des Phonèmes"},
120
+ "description": {"en": "Master specific sounds like R, S, TH", "fr": "Maîtrisez des sons spécifiques comme R, S, CH"}
121
+ },
122
+ "word_repetition": {
123
+ "name": {"en": "Word Repetition", "fr": "Répétition de Mots"},
124
+ "description": {"en": "Repeat words clearly", "fr": "Répétez les mots clairement"}
125
+ },
126
+ "sentence_reading": {
127
+ "name": {"en": "Sentence Reading", "fr": "Lecture de Phrases"},
128
+ "description": {"en": "Read complete sentences", "fr": "Lisez des phrases complètes"}
129
+ },
130
+ "tongue_twister": {
131
+ "name": {"en": "Tongue Twisters", "fr": "Virelangues"},
132
+ "description": {"en": "Challenge yourself with tricky phrases", "fr": "Défiez-vous avec des phrases difficiles"}
133
+ },
134
+ "color": {
135
+ "name": {"en": "Colors", "fr": "Couleurs"},
136
+ "description": {"en": "Identify and say color names", "fr": "Identifiez et dites les noms des couleurs"}
137
+ },
138
+ "object": {
139
+ "name": {"en": "Objects", "fr": "Objets"},
140
+ "description": {"en": "Name everyday objects", "fr": "Nommez des objets du quotidien"}
141
+ },
142
+ "animal": {
143
+ "name": {"en": "Animals", "fr": "Animaux"},
144
+ "description": {"en": "Identify animals by sight", "fr": "Identifiez les animaux à vue"}
145
+ },
146
+ "action": {
147
+ "name": {"en": "Actions", "fr": "Actions"},
148
+ "description": {"en": "Describe what people are doing", "fr": "Décrivez ce que font les gens"}
149
+ },
150
+ "animal_sound": {
151
+ "name": {"en": "Animal Sounds", "fr": "Sons d'Animaux"},
152
+ "description": {"en": "Imitate animal sounds", "fr": "Imitez les sons des animaux"}
153
+ },
154
+ "environmental_sound": {
155
+ "name": {"en": "Environmental Sounds", "fr": "Sons de l'Environnement"},
156
+ "description": {"en": "Imitate sounds around us", "fr": "Imitez les sons autour de nous"}
157
+ }
158
+ }
159
+
160
+
161
+ # =============================================================================
162
+ # EXERCISES DATABASE
163
+ # =============================================================================
164
+
165
+ EXERCISES: List[Dict] = [
166
+ # =========================================================================
167
+ # FUNDAMENTALS - Breathing
168
+ # =========================================================================
169
+ {
170
+ "id": "breath-001",
171
+ "type": "breathing",
172
+ "category": "fundamentals",
173
+ "subcategory": "breathing",
174
+ "difficulty": "easy",
175
+ "title": {"en": "Deep Belly Breathing", "fr": "Respiration Abdominale"},
176
+ "target_text": {"en": "Breathe in slowly through your nose, hold, breathe out through your mouth", "fr": "Inspirez lentement par le nez, retenez, expirez par la bouche"},
177
+ "instructions": {"en": "Place your hand on your belly. Breathe in for 4 seconds, hold for 2, breathe out for 4. Repeat 3 times.", "fr": "Placez votre main sur le ventre. Inspirez 4 secondes, retenez 2, expirez 4. Répétez 3 fois."},
178
+ },
179
+ {
180
+ "id": "breath-002",
181
+ "type": "breathing",
182
+ "category": "fundamentals",
183
+ "subcategory": "breathing",
184
+ "difficulty": "easy",
185
+ "title": {"en": "Candle Blow", "fr": "Souffler la Bougie"},
186
+ "target_text": {"en": "Take a deep breath and blow out slowly like blowing a candle", "fr": "Prenez une grande inspiration et soufflez doucement comme une bougie"},
187
+ "instructions": {"en": "Imagine a candle in front of you. Take a deep breath and blow slowly to make the flame flicker but not go out.", "fr": "Imaginez une bougie devant vous. Inspirez et soufflez doucement pour faire vaciller la flamme sans l'éteindre."},
188
+ },
189
+ {
190
+ "id": "breath-003",
191
+ "type": "breathing",
192
+ "category": "fundamentals",
193
+ "subcategory": "breathing",
194
+ "difficulty": "medium",
195
+ "title": {"en": "Sustained Breath", "fr": "Souffle Prolongé"},
196
+ "target_text": {"en": "Aaaaaaaaahhhhhh", "fr": "Aaaaaaaaahhhhhh"},
197
+ "instructions": {"en": "Take a deep breath and say 'Ahhh' for as long as you can. Try to reach 10 seconds!", "fr": "Inspirez profondément et dites 'Ahhh' aussi longtemps que possible. Essayez d'atteindre 10 secondes!"},
198
+ },
199
+
200
+ # =========================================================================
201
+ # FUNDAMENTALS - Articulation
202
+ # =========================================================================
203
+ {
204
+ "id": "artic-001",
205
+ "type": "articulation",
206
+ "category": "fundamentals",
207
+ "subcategory": "articulation",
208
+ "difficulty": "easy",
209
+ "title": {"en": "Lip Warm-up", "fr": "Échauffement des Lèvres"},
210
+ "target_text": {"en": "Ma ma ma, Pa pa pa, Ba ba ba", "fr": "Ma ma ma, Pa pa pa, Ba ba ba"},
211
+ "instructions": {"en": "Say each syllable clearly, focusing on your lip movements. Repeat 3 times.", "fr": "Prononcez chaque syllabe clairement en vous concentrant sur vos lèvres. Répétez 3 fois."},
212
+ },
213
+ {
214
+ "id": "artic-002",
215
+ "type": "articulation",
216
+ "category": "fundamentals",
217
+ "subcategory": "articulation",
218
+ "difficulty": "easy",
219
+ "title": {"en": "Tongue Stretch", "fr": "Étirement de la Langue"},
220
+ "target_text": {"en": "La la la, Ta ta ta, Da da da", "fr": "La la la, Ta ta ta, Da da da"},
221
+ "instructions": {"en": "Touch the roof of your mouth with your tongue for each syllable. Feel the movement!", "fr": "Touchez le palais avec votre langue pour chaque syllabe. Sentez le mouvement!"},
222
+ },
223
+ {
224
+ "id": "artic-003",
225
+ "type": "articulation",
226
+ "category": "fundamentals",
227
+ "subcategory": "articulation",
228
+ "difficulty": "medium",
229
+ "title": {"en": "Jaw Exercise", "fr": "Exercice de Mâchoire"},
230
+ "target_text": {"en": "Wa wa wa, Ya ya ya, Oo ee oo ee", "fr": "Oua oua oua, Ya ya ya, Ou i ou i"},
231
+ "instructions": {"en": "Open your mouth wide for each sound. Feel your jaw moving up and down.", "fr": "Ouvrez grand la bouche pour chaque son. Sentez votre mâchoire bouger."},
232
+ },
233
+
234
+ # =========================================================================
235
+ # FUNDAMENTALS - Phoneme Practice
236
+ # =========================================================================
237
+ {
238
+ "id": "phon-r-001",
239
+ "type": "phoneme",
240
+ "category": "fundamentals",
241
+ "subcategory": "phoneme",
242
+ "difficulty": "medium",
243
+ "title": {"en": "R Sound Practice", "fr": "Pratique du Son R"},
244
+ "target_text": {"en": "Run, red, rain, rabbit, river", "fr": "Rue, rouge, rire, rat, rivière"},
245
+ "instructions": {"en": "Focus on the 'R' sound at the beginning of each word. Let your tongue vibrate slightly.", "fr": "Concentrez-vous sur le son 'R' au début de chaque mot. Laissez votre langue vibrer légèrement."},
246
+ "phoneme_focus": ["R"]
247
+ },
248
+ {
249
+ "id": "phon-s-001",
250
+ "type": "phoneme",
251
+ "category": "fundamentals",
252
+ "subcategory": "phoneme",
253
+ "difficulty": "easy",
254
+ "title": {"en": "S Sound Practice", "fr": "Pratique du Son S"},
255
+ "target_text": {"en": "Sun, see, song, seven, smile", "fr": "Sol, sac, sept, soir, sourire"},
256
+ "instructions": {"en": "Make a hissing sound like a snake. Keep your tongue behind your teeth.", "fr": "Faites un son sifflant comme un serpent. Gardez la langue derrière les dents."},
257
+ "phoneme_focus": ["S"]
258
+ },
259
+ {
260
+ "id": "phon-th-001",
261
+ "type": "phoneme",
262
+ "category": "fundamentals",
263
+ "subcategory": "phoneme",
264
+ "difficulty": "hard",
265
+ "title": {"en": "TH Sound Practice", "fr": "Pratique du Son TH"},
266
+ "target_text": {"en": "Think, this, that, three, thank", "fr": "Think, this, that, three, thank"},
267
+ "instructions": {"en": "Put your tongue between your teeth and blow air. This sound doesn't exist in French!", "fr": "Mettez la langue entre les dents et soufflez. Ce son n'existe pas en français!"},
268
+ "phoneme_focus": ["TH"]
269
+ },
270
+ {
271
+ "id": "phon-l-001",
272
+ "type": "phoneme",
273
+ "category": "fundamentals",
274
+ "subcategory": "phoneme",
275
+ "difficulty": "easy",
276
+ "title": {"en": "L Sound Practice", "fr": "Pratique du Son L"},
277
+ "target_text": {"en": "Love, light, look, lion, lake", "fr": "Lune, livre, lac, lion, lait"},
278
+ "instructions": {"en": "Touch the roof of your mouth with the tip of your tongue.", "fr": "Touchez le palais avec le bout de la langue."},
279
+ "phoneme_focus": ["L"]
280
+ },
281
+ {
282
+ "id": "phon-ch-001",
283
+ "type": "phoneme",
284
+ "category": "fundamentals",
285
+ "subcategory": "phoneme",
286
+ "difficulty": "medium",
287
+ "title": {"en": "CH Sound Practice", "fr": "Pratique du Son CH"},
288
+ "target_text": {"en": "Chair, cheese, chicken, chocolate, church", "fr": "Chat, chien, chose, chou, chaud"},
289
+ "instructions": {"en": "Push air through your teeth with rounded lips.", "fr": "Poussez l'air entre vos dents avec les lèvres arrondies."},
290
+ "phoneme_focus": ["CH", "SH"]
291
+ },
292
+
293
+ # =========================================================================
294
+ # SPEECH PRACTICE - Word Repetition
295
+ # =========================================================================
296
+ {
297
+ "id": "word-001",
298
+ "type": "word_repetition",
299
+ "category": "speech_practice",
300
+ "subcategory": "word_repetition",
301
+ "difficulty": "easy",
302
+ "title": {"en": "Greetings", "fr": "Salutations"},
303
+ "target_text": {"en": "Hello, Goodbye, Please, Thank you", "fr": "Bonjour, Au revoir, S'il vous plaît, Merci"},
304
+ "instructions": {"en": "Say each word clearly with a pause between them.", "fr": "Dites chaque mot clairement avec une pause entre eux."},
305
+ },
306
+ {
307
+ "id": "word-002",
308
+ "type": "word_repetition",
309
+ "category": "speech_practice",
310
+ "subcategory": "word_repetition",
311
+ "difficulty": "easy",
312
+ "title": {"en": "Numbers 1-10", "fr": "Chiffres 1-10"},
313
+ "target_text": {"en": "One, two, three, four, five, six, seven, eight, nine, ten", "fr": "Un, deux, trois, quatre, cinq, six, sept, huit, neuf, dix"},
314
+ "instructions": {"en": "Count clearly and steadily. Don't rush!", "fr": "Comptez clairement et régulièrement. Ne vous précipitez pas!"},
315
+ },
316
+ {
317
+ "id": "word-003",
318
+ "type": "word_repetition",
319
+ "category": "speech_practice",
320
+ "subcategory": "word_repetition",
321
+ "difficulty": "medium",
322
+ "title": {"en": "Days of the Week", "fr": "Jours de la Semaine"},
323
+ "target_text": {"en": "Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday", "fr": "Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi, Dimanche"},
324
+ "instructions": {"en": "Say each day with emphasis on the first syllable.", "fr": "Dites chaque jour en accentuant la première syllabe."},
325
+ },
326
+
327
+ # =========================================================================
328
+ # SPEECH PRACTICE - Sentence Reading
329
+ # =========================================================================
330
+ {
331
+ "id": "sent-001",
332
+ "type": "sentence_reading",
333
+ "category": "speech_practice",
334
+ "subcategory": "sentence_reading",
335
+ "difficulty": "easy",
336
+ "title": {"en": "Simple Introduction", "fr": "Introduction Simple"},
337
+ "target_text": {"en": "Hello, my name is... Nice to meet you!", "fr": "Bonjour, je m'appelle... Enchanté!"},
338
+ "instructions": {"en": "Replace '...' with your name. Speak clearly and smile!", "fr": "Remplacez '...' par votre nom. Parlez clairement et souriez!"},
339
+ },
340
+ {
341
+ "id": "sent-002",
342
+ "type": "sentence_reading",
343
+ "category": "speech_practice",
344
+ "subcategory": "sentence_reading",
345
+ "difficulty": "easy",
346
+ "title": {"en": "How Are You?", "fr": "Comment Allez-Vous?"},
347
+ "target_text": {"en": "How are you today? I am fine, thank you.", "fr": "Comment allez-vous aujourd'hui? Je vais bien, merci."},
348
+ "instructions": {"en": "Practice this common conversation with natural intonation.", "fr": "Pratiquez cette conversation courante avec une intonation naturelle."},
349
+ },
350
+ {
351
+ "id": "sent-003",
352
+ "type": "sentence_reading",
353
+ "category": "speech_practice",
354
+ "subcategory": "sentence_reading",
355
+ "difficulty": "medium",
356
+ "title": {"en": "Weather Talk", "fr": "Parler de la Météo"},
357
+ "target_text": {"en": "The weather is beautiful today. The sun is shining brightly.", "fr": "Il fait beau aujourd'hui. Le soleil brille."},
358
+ "instructions": {"en": "Read the sentences with expression. Sound happy about the weather!", "fr": "Lisez avec expression. Montrez que vous êtes content du temps!"},
359
+ },
360
+ {
361
+ "id": "sent-004",
362
+ "type": "sentence_reading",
363
+ "category": "speech_practice",
364
+ "subcategory": "sentence_reading",
365
+ "difficulty": "hard",
366
+ "title": {"en": "At the Restaurant", "fr": "Au Restaurant"},
367
+ "target_text": {"en": "I would like to order the fish, please. Could I also have some water?", "fr": "Je voudrais commander le poisson, s'il vous plaît. Puis-je aussi avoir de l'eau?"},
368
+ "instructions": {"en": "Practice polite restaurant phrases. Speak clearly to be understood.", "fr": "Pratiquez les phrases polies au restaurant. Parlez clairement pour être compris."},
369
+ },
370
+
371
+ # =========================================================================
372
+ # SPEECH PRACTICE - Tongue Twisters
373
+ # =========================================================================
374
+ {
375
+ "id": "twist-001",
376
+ "type": "tongue_twister",
377
+ "category": "speech_practice",
378
+ "subcategory": "tongue_twister",
379
+ "difficulty": "medium",
380
+ "title": {"en": "She Sells Seashells", "fr": "Les Chaussettes de l'Archiduchesse"},
381
+ "target_text": {"en": "She sells seashells by the seashore", "fr": "Les chaussettes de l'archiduchesse sont-elles sèches ou archi-sèches"},
382
+ "instructions": {"en": "Start slowly, then try to speed up while staying clear.", "fr": "Commencez lentement, puis essayez d'accélérer tout en restant clair."},
383
+ "phoneme_focus": ["S", "SH"]
384
+ },
385
+ {
386
+ "id": "twist-002",
387
+ "type": "tongue_twister",
388
+ "category": "speech_practice",
389
+ "subcategory": "tongue_twister",
390
+ "difficulty": "hard",
391
+ "title": {"en": "Peter Piper", "fr": "Un Chasseur"},
392
+ "target_text": {"en": "Peter Piper picked a peck of pickled peppers", "fr": "Un chasseur sachant chasser sait chasser sans son chien"},
393
+ "instructions": {"en": "Focus on the 'P' sounds. Keep your lips tight.", "fr": "Concentrez-vous sur les sons 'CH'. Gardez la langue stable."},
394
+ "phoneme_focus": ["P"]
395
+ },
396
+ {
397
+ "id": "twist-003",
398
+ "type": "tongue_twister",
399
+ "category": "speech_practice",
400
+ "subcategory": "tongue_twister",
401
+ "difficulty": "hard",
402
+ "title": {"en": "Red Lorry Yellow Lorry", "fr": "Trois Tortues"},
403
+ "target_text": {"en": "Red lorry, yellow lorry, red lorry, yellow lorry", "fr": "Trois tortues trottaient sur trois toits très étroits"},
404
+ "instructions": {"en": "Practice the R and L sounds alternating. This is challenging!", "fr": "Pratiquez les sons T et R en alternance. C'est un défi!"},
405
+ "phoneme_focus": ["R", "L"]
406
+ },
407
+ {
408
+ "id": "twist-004",
409
+ "type": "tongue_twister",
410
+ "category": "speech_practice",
411
+ "subcategory": "tongue_twister",
412
+ "difficulty": "medium",
413
+ "title": {"en": "Unique New York", "fr": "Panier Piano"},
414
+ "target_text": {"en": "Unique New York, you know you need unique New York", "fr": "Piano panier, panier piano"},
415
+ "instructions": {"en": "Focus on the 'N' and 'Y' sounds.", "fr": "Concentrez-vous sur les sons 'P' et 'N'."},
416
+ "phoneme_focus": ["N", "Y"]
417
+ },
418
+
419
+ # =========================================================================
420
+ # VISUAL LEARNING - Colors
421
+ # =========================================================================
422
+ {
423
+ "id": "color-001",
424
+ "type": "color",
425
+ "category": "visual_learning",
426
+ "subcategory": "color",
427
+ "difficulty": "easy",
428
+ "title": {"en": "Red", "fr": "Rouge"},
429
+ "target_text": {"en": "Red", "fr": "Rouge"},
430
+ "instructions": {"en": "Look at the color and say its name.", "fr": "Regardez la couleur et dites son nom."},
431
+ "image_url": "https://images.unsplash.com/photo-1562157873-818bc0726f68?w=400&h=400&fit=crop"
432
+ },
433
+ {
434
+ "id": "color-002",
435
+ "type": "color",
436
+ "category": "visual_learning",
437
+ "subcategory": "color",
438
+ "difficulty": "easy",
439
+ "title": {"en": "Blue", "fr": "Bleu"},
440
+ "target_text": {"en": "Blue", "fr": "Bleu"},
441
+ "instructions": {"en": "Look at the color and say its name.", "fr": "Regardez la couleur et dites son nom."},
442
+ "image_url": "https://images.unsplash.com/photo-1579546929518-9e396f3cc809?w=400&h=400&fit=crop"
443
+ },
444
+ {
445
+ "id": "color-003",
446
+ "type": "color",
447
+ "category": "visual_learning",
448
+ "subcategory": "color",
449
+ "difficulty": "easy",
450
+ "title": {"en": "Yellow", "fr": "Jaune"},
451
+ "target_text": {"en": "Yellow", "fr": "Jaune"},
452
+ "instructions": {"en": "Look at the color and say its name.", "fr": "Regardez la couleur et dites son nom."},
453
+ "image_url": "https://images.unsplash.com/photo-1495001258031-d1b407bc1776?w=400&h=400&fit=crop"
454
+ },
455
+ {
456
+ "id": "color-004",
457
+ "type": "color",
458
+ "category": "visual_learning",
459
+ "subcategory": "color",
460
+ "difficulty": "easy",
461
+ "title": {"en": "Green", "fr": "Vert"},
462
+ "target_text": {"en": "Green", "fr": "Vert"},
463
+ "instructions": {"en": "Look at the color and say its name.", "fr": "Regardez la couleur et dites son nom."},
464
+ "image_url": "https://images.unsplash.com/photo-1464820453369-31d2c0b651af?w=400&h=400&fit=crop"
465
+ },
466
+ {
467
+ "id": "color-005",
468
+ "type": "color",
469
+ "category": "visual_learning",
470
+ "subcategory": "color",
471
+ "difficulty": "easy",
472
+ "title": {"en": "Orange", "fr": "Orange"},
473
+ "target_text": {"en": "Orange", "fr": "Orange"},
474
+ "instructions": {"en": "Look at the color and say its name.", "fr": "Regardez la couleur et dites son nom."},
475
+ "image_url": "https://images.unsplash.com/photo-1557800636-894a64c1696f?w=400&h=400&fit=crop"
476
+ },
477
+ {
478
+ "id": "color-006",
479
+ "type": "color",
480
+ "category": "visual_learning",
481
+ "subcategory": "color",
482
+ "difficulty": "easy",
483
+ "title": {"en": "Purple", "fr": "Violet"},
484
+ "target_text": {"en": "Purple", "fr": "Violet"},
485
+ "instructions": {"en": "Look at the color and say its name.", "fr": "Regardez la couleur et dites son nom."},
486
+ "image_url": "https://images.unsplash.com/photo-1528459801416-a9e53bbf4e17?w=400&h=400&fit=crop"
487
+ },
488
+
489
+ # =========================================================================
490
+ # VISUAL LEARNING - Objects
491
+ # =========================================================================
492
+ {
493
+ "id": "obj-001",
494
+ "type": "object",
495
+ "category": "visual_learning",
496
+ "subcategory": "object",
497
+ "difficulty": "easy",
498
+ "title": {"en": "Apple", "fr": "Pomme"},
499
+ "target_text": {"en": "Apple", "fr": "Pomme"},
500
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
501
+ "image_url": "https://images.unsplash.com/photo-1584306670957-acf935f5033c?w=400&h=400&fit=crop"
502
+ },
503
+ {
504
+ "id": "obj-002",
505
+ "type": "object",
506
+ "category": "visual_learning",
507
+ "subcategory": "object",
508
+ "difficulty": "easy",
509
+ "title": {"en": "Book", "fr": "Livre"},
510
+ "target_text": {"en": "Book", "fr": "Livre"},
511
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
512
+ "image_url": "https://images.unsplash.com/photo-1544947950-fa07a98d237f?w=400&h=400&fit=crop"
513
+ },
514
+ {
515
+ "id": "obj-003",
516
+ "type": "object",
517
+ "category": "visual_learning",
518
+ "subcategory": "object",
519
+ "difficulty": "easy",
520
+ "title": {"en": "Car", "fr": "Voiture"},
521
+ "target_text": {"en": "Car", "fr": "Voiture"},
522
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
523
+ "image_url": "https://images.unsplash.com/photo-1502877338535-766e1452684a?w=400&h=400&fit=crop"
524
+ },
525
+ {
526
+ "id": "obj-004",
527
+ "type": "object",
528
+ "category": "visual_learning",
529
+ "subcategory": "object",
530
+ "difficulty": "easy",
531
+ "title": {"en": "Chair", "fr": "Chaise"},
532
+ "target_text": {"en": "Chair", "fr": "Chaise"},
533
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
534
+ "image_url": "https://images.unsplash.com/photo-1503602642458-232111445657?w=400&h=400&fit=crop"
535
+ },
536
+ {
537
+ "id": "obj-005",
538
+ "type": "object",
539
+ "category": "visual_learning",
540
+ "subcategory": "object",
541
+ "difficulty": "easy",
542
+ "title": {"en": "House", "fr": "Maison"},
543
+ "target_text": {"en": "House", "fr": "Maison"},
544
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
545
+ "image_url": "https://images.unsplash.com/photo-1518780664697-55e3ad937233?w=400&h=400&fit=crop"
546
+ },
547
+ {
548
+ "id": "obj-006",
549
+ "type": "object",
550
+ "category": "visual_learning",
551
+ "subcategory": "object",
552
+ "difficulty": "medium",
553
+ "title": {"en": "Telephone", "fr": "Téléphone"},
554
+ "target_text": {"en": "Telephone", "fr": "Téléphone"},
555
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
556
+ "image_url": "https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=400&h=400&fit=crop"
557
+ },
558
+ {
559
+ "id": "obj-007",
560
+ "type": "object",
561
+ "category": "visual_learning",
562
+ "subcategory": "object",
563
+ "difficulty": "medium",
564
+ "title": {"en": "Umbrella", "fr": "Parapluie"},
565
+ "target_text": {"en": "Umbrella", "fr": "Parapluie"},
566
+ "instructions": {"en": "Look at the picture and say what you see.", "fr": "Regardez l'image et dites ce que vous voyez."},
567
+ "image_url": "https://images.unsplash.com/photo-1534309466160-70b22cc6252c?w=400&h=400&fit=crop"
568
+ },
569
+
570
+ # =========================================================================
571
+ # VISUAL LEARNING - Animals
572
+ # =========================================================================
573
+ {
574
+ "id": "animal-001",
575
+ "type": "animal",
576
+ "category": "visual_learning",
577
+ "subcategory": "animal",
578
+ "difficulty": "easy",
579
+ "title": {"en": "Dog", "fr": "Chien"},
580
+ "target_text": {"en": "Dog", "fr": "Chien"},
581
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
582
+ "image_url": "https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=400&h=400&fit=crop"
583
+ },
584
+ {
585
+ "id": "animal-002",
586
+ "type": "animal",
587
+ "category": "visual_learning",
588
+ "subcategory": "animal",
589
+ "difficulty": "easy",
590
+ "title": {"en": "Cat", "fr": "Chat"},
591
+ "target_text": {"en": "Cat", "fr": "Chat"},
592
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
593
+ "image_url": "https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=400&h=400&fit=crop"
594
+ },
595
+ {
596
+ "id": "animal-003",
597
+ "type": "animal",
598
+ "category": "visual_learning",
599
+ "subcategory": "animal",
600
+ "difficulty": "easy",
601
+ "title": {"en": "Bird", "fr": "Oiseau"},
602
+ "target_text": {"en": "Bird", "fr": "Oiseau"},
603
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
604
+ "image_url": "https://images.unsplash.com/photo-1522926193341-e9ffd686c60f?w=400&h=400&fit=crop"
605
+ },
606
+ {
607
+ "id": "animal-004",
608
+ "type": "animal",
609
+ "category": "visual_learning",
610
+ "subcategory": "animal",
611
+ "difficulty": "easy",
612
+ "title": {"en": "Fish", "fr": "Poisson"},
613
+ "target_text": {"en": "Fish", "fr": "Poisson"},
614
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
615
+ "image_url": "https://images.unsplash.com/photo-1524704654690-b56c05c78a00?w=400&h=400&fit=crop"
616
+ },
617
+ {
618
+ "id": "animal-005",
619
+ "type": "animal",
620
+ "category": "visual_learning",
621
+ "subcategory": "animal",
622
+ "difficulty": "medium",
623
+ "title": {"en": "Elephant", "fr": "Éléphant"},
624
+ "target_text": {"en": "Elephant", "fr": "Éléphant"},
625
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
626
+ "image_url": "https://images.unsplash.com/photo-1557050543-4d5f4e07ef46?w=400&h=400&fit=crop"
627
+ },
628
+ {
629
+ "id": "animal-006",
630
+ "type": "animal",
631
+ "category": "visual_learning",
632
+ "subcategory": "animal",
633
+ "difficulty": "medium",
634
+ "title": {"en": "Lion", "fr": "Lion"},
635
+ "target_text": {"en": "Lion", "fr": "Lion"},
636
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
637
+ "image_url": "https://images.unsplash.com/photo-1546182990-dffeafbe841d?w=400&h=400&fit=crop"
638
+ },
639
+ {
640
+ "id": "animal-007",
641
+ "type": "animal",
642
+ "category": "visual_learning",
643
+ "subcategory": "animal",
644
+ "difficulty": "medium",
645
+ "title": {"en": "Butterfly", "fr": "Papillon"},
646
+ "target_text": {"en": "Butterfly", "fr": "Papillon"},
647
+ "instructions": {"en": "Look at the animal and say its name.", "fr": "Regardez l'animal et dites son nom."},
648
+ "image_url": "https://images.unsplash.com/photo-1452570053594-1b985d6ea890?w=400&h=400&fit=crop"
649
+ },
650
+ {
651
+ "id": "animal-008",
652
+ "type": "animal",
653
+ "category": "visual_learning",
654
+ "subcategory": "animal",
655
+ "difficulty": "hard",
656
+ "title": {"en": "Hippopotamus", "fr": "Hippopotame"},
657
+ "target_text": {"en": "Hippopotamus", "fr": "Hippopotame"},
658
+ "instructions": {"en": "Look at the animal and say its name. This is a long word!", "fr": "Regardez l'animal et dites son nom. C'est un mot long!"},
659
+ "image_url": "https://images.unsplash.com/photo-1517840933437-c41356892b35?w=400&h=400&fit=crop"
660
+ },
661
+
662
+ # =========================================================================
663
+ # VISUAL LEARNING - Actions
664
+ # =========================================================================
665
+ {
666
+ "id": "action-001",
667
+ "type": "action",
668
+ "category": "visual_learning",
669
+ "subcategory": "action",
670
+ "difficulty": "easy",
671
+ "title": {"en": "Running", "fr": "Courir"},
672
+ "target_text": {"en": "Running", "fr": "Courir"},
673
+ "instructions": {"en": "Look at the action and say what the person is doing.", "fr": "Regardez l'action et dites ce que fait la personne."},
674
+ "image_url": "https://images.unsplash.com/photo-1552674605-db6ffd4facb5?w=400&h=400&fit=crop"
675
+ },
676
+ {
677
+ "id": "action-002",
678
+ "type": "action",
679
+ "category": "visual_learning",
680
+ "subcategory": "action",
681
+ "difficulty": "easy",
682
+ "title": {"en": "Eating", "fr": "Manger"},
683
+ "target_text": {"en": "Eating", "fr": "Manger"},
684
+ "instructions": {"en": "Look at the action and say what the person is doing.", "fr": "Regardez l'action et dites ce que fait la personne."},
685
+ "image_url": "https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=400&h=400&fit=crop"
686
+ },
687
+ {
688
+ "id": "action-003",
689
+ "type": "action",
690
+ "category": "visual_learning",
691
+ "subcategory": "action",
692
+ "difficulty": "easy",
693
+ "title": {"en": "Sleeping", "fr": "Dormir"},
694
+ "target_text": {"en": "Sleeping", "fr": "Dormir"},
695
+ "instructions": {"en": "Look at the action and say what the person is doing.", "fr": "Regardez l'action et dites ce que fait la personne."},
696
+ "image_url": "https://images.unsplash.com/photo-1541781774459-bb2af2f05b55?w=400&h=400&fit=crop"
697
+ },
698
+ {
699
+ "id": "action-004",
700
+ "type": "action",
701
+ "category": "visual_learning",
702
+ "subcategory": "action",
703
+ "difficulty": "easy",
704
+ "title": {"en": "Reading", "fr": "Lire"},
705
+ "target_text": {"en": "Reading", "fr": "Lire"},
706
+ "instructions": {"en": "Look at the action and say what the person is doing.", "fr": "Regardez l'action et dites ce que fait la personne."},
707
+ "image_url": "https://images.unsplash.com/photo-1506880018603-83d5b814b5a6?w=400&h=400&fit=crop"
708
+ },
709
+ {
710
+ "id": "action-005",
711
+ "type": "action",
712
+ "category": "visual_learning",
713
+ "subcategory": "action",
714
+ "difficulty": "medium",
715
+ "title": {"en": "Swimming", "fr": "Nager"},
716
+ "target_text": {"en": "Swimming", "fr": "Nager"},
717
+ "instructions": {"en": "Look at the action and say what the person is doing.", "fr": "Regardez l'action et dites ce que fait la personne."},
718
+ "image_url": "https://images.unsplash.com/photo-1530549387789-4c1017266635?w=400&h=400&fit=crop"
719
+ },
720
+
721
+ # =========================================================================
722
+ # SOUND IMITATION - Animal Sounds
723
+ # =========================================================================
724
+ {
725
+ "id": "asound-001",
726
+ "type": "animal_sound",
727
+ "category": "sound_imitation",
728
+ "subcategory": "animal_sound",
729
+ "difficulty": "easy",
730
+ "title": {"en": "Dog Sound", "fr": "Son du Chien"},
731
+ "target_text": {"en": "Woof woof", "fr": "Ouaf ouaf"},
732
+ "instructions": {"en": "Imitate the sound a dog makes!", "fr": "Imitez le son que fait un chien!"},
733
+ "image_url": "https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=400&h=400&fit=crop"
734
+ },
735
+ {
736
+ "id": "asound-002",
737
+ "type": "animal_sound",
738
+ "category": "sound_imitation",
739
+ "subcategory": "animal_sound",
740
+ "difficulty": "easy",
741
+ "title": {"en": "Cat Sound", "fr": "Son du Chat"},
742
+ "target_text": {"en": "Meow meow", "fr": "Miaou miaou"},
743
+ "instructions": {"en": "Imitate the sound a cat makes!", "fr": "Imitez le son que fait un chat!"},
744
+ "image_url": "https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=400&h=400&fit=crop"
745
+ },
746
+ {
747
+ "id": "asound-003",
748
+ "type": "animal_sound",
749
+ "category": "sound_imitation",
750
+ "subcategory": "animal_sound",
751
+ "difficulty": "easy",
752
+ "title": {"en": "Cow Sound", "fr": "Son de la Vache"},
753
+ "target_text": {"en": "Moo moo", "fr": "Meuh meuh"},
754
+ "instructions": {"en": "Imitate the sound a cow makes!", "fr": "Imitez le son que fait une vache!"},
755
+ "image_url": "https://images.unsplash.com/photo-1570042225831-d98fa7577f1e?w=400&h=400&fit=crop"
756
+ },
757
+ {
758
+ "id": "asound-004",
759
+ "type": "animal_sound",
760
+ "category": "sound_imitation",
761
+ "subcategory": "animal_sound",
762
+ "difficulty": "easy",
763
+ "title": {"en": "Duck Sound", "fr": "Son du Canard"},
764
+ "target_text": {"en": "Quack quack", "fr": "Coin coin"},
765
+ "instructions": {"en": "Imitate the sound a duck makes!", "fr": "Imitez le son que fait un canard!"},
766
+ "image_url": "https://images.unsplash.com/photo-1459682687441-7761439a709d?w=400&h=400&fit=crop"
767
+ },
768
+ {
769
+ "id": "asound-005",
770
+ "type": "animal_sound",
771
+ "category": "sound_imitation",
772
+ "subcategory": "animal_sound",
773
+ "difficulty": "medium",
774
+ "title": {"en": "Lion Sound", "fr": "Son du Lion"},
775
+ "target_text": {"en": "Roar!", "fr": "Grrrr!"},
776
+ "instructions": {"en": "Imitate the sound a lion makes! Be loud!", "fr": "Imitez le son que fait un lion! Soyez fort!"},
777
+ "image_url": "https://images.unsplash.com/photo-1546182990-dffeafbe841d?w=400&h=400&fit=crop"
778
+ },
779
+ {
780
+ "id": "asound-006",
781
+ "type": "animal_sound",
782
+ "category": "sound_imitation",
783
+ "subcategory": "animal_sound",
784
+ "difficulty": "medium",
785
+ "title": {"en": "Snake Sound", "fr": "Son du Serpent"},
786
+ "target_text": {"en": "Sssssss", "fr": "Sssssss"},
787
+ "instructions": {"en": "Imitate the hissing sound a snake makes!", "fr": "Imitez le sifflement que fait un serpent!"},
788
+ "image_url": "https://images.unsplash.com/photo-1531386151447-fd76ad50012f?w=400&h=400&fit=crop"
789
+ },
790
+
791
+ # =========================================================================
792
+ # SOUND IMITATION - Environmental Sounds
793
+ # =========================================================================
794
+ {
795
+ "id": "esound-001",
796
+ "type": "environmental_sound",
797
+ "category": "sound_imitation",
798
+ "subcategory": "environmental_sound",
799
+ "difficulty": "easy",
800
+ "title": {"en": "Car Horn", "fr": "Klaxon"},
801
+ "target_text": {"en": "Beep beep", "fr": "Pouet pouet"},
802
+ "instructions": {"en": "Imitate the sound of a car horn!", "fr": "Imitez le son d'un klaxon!"},
803
+ "image_url": "https://images.unsplash.com/photo-1502877338535-766e1452684a?w=400&h=400&fit=crop"
804
+ },
805
+ {
806
+ "id": "esound-002",
807
+ "type": "environmental_sound",
808
+ "category": "sound_imitation",
809
+ "subcategory": "environmental_sound",
810
+ "difficulty": "easy",
811
+ "title": {"en": "Train Sound", "fr": "Son du Train"},
812
+ "target_text": {"en": "Choo choo", "fr": "Tchou tchou"},
813
+ "instructions": {"en": "Imitate the sound of a train!", "fr": "Imitez le son d'un train!"},
814
+ "image_url": "https://images.unsplash.com/photo-1474487548417-781cb71495f3?w=400&h=400&fit=crop"
815
+ },
816
+ {
817
+ "id": "esound-003",
818
+ "type": "environmental_sound",
819
+ "category": "sound_imitation",
820
+ "subcategory": "environmental_sound",
821
+ "difficulty": "easy",
822
+ "title": {"en": "Clock Sound", "fr": "Son de l'Horloge"},
823
+ "target_text": {"en": "Tick tock tick tock", "fr": "Tic tac tic tac"},
824
+ "instructions": {"en": "Imitate the sound of a clock!", "fr": "Imitez le son d'une horloge!"},
825
+ "image_url": "https://images.unsplash.com/photo-1563861826100-9cb868fdbe1c?w=400&h=400&fit=crop"
826
+ },
827
+ {
828
+ "id": "esound-004",
829
+ "type": "environmental_sound",
830
+ "category": "sound_imitation",
831
+ "subcategory": "environmental_sound",
832
+ "difficulty": "medium",
833
+ "title": {"en": "Rain Sound", "fr": "Son de la Pluie"},
834
+ "target_text": {"en": "Pitter patter pitter patter", "fr": "Plic ploc plic ploc"},
835
+ "instructions": {"en": "Imitate the sound of rain falling!", "fr": "Imitez le son de la pluie qui tombe!"},
836
+ "image_url": "https://images.unsplash.com/photo-1519692933481-e162a57d6721?w=400&h=400&fit=crop"
837
+ },
838
+ {
839
+ "id": "esound-005",
840
+ "type": "environmental_sound",
841
+ "category": "sound_imitation",
842
+ "subcategory": "environmental_sound",
843
+ "difficulty": "medium",
844
+ "title": {"en": "Wind Sound", "fr": "Son du Vent"},
845
+ "target_text": {"en": "Whoooosh", "fr": "Woooosh"},
846
+ "instructions": {"en": "Imitate the sound of strong wind!", "fr": "Imitez le son du vent fort!"},
847
+ "image_url": "https://images.unsplash.com/photo-1534088568595-a066f410bcda?w=400&h=400&fit=crop"
848
+ },
849
+ ]
850
+
851
+
852
+ def get_all_categories() -> List[Dict]:
853
+ """Return all exercise categories."""
854
+ return CATEGORIES
855
+
856
+
857
+ def get_subcategory_info(subcategory_id: str) -> Optional[Dict]:
858
+ """Return information about a specific subcategory."""
859
+ return SUBCATEGORIES.get(subcategory_id)
860
+
861
+
862
+ def get_all_exercises(language: str = "en") -> List[Dict]:
863
+ """Return all exercises in the specified language."""
864
+ exercises = []
865
+ for ex in EXERCISES:
866
+ exercises.append({
867
+ "id": ex["id"],
868
+ "type": ex["type"],
869
+ "category": ex["category"],
870
+ "subcategory": ex["subcategory"],
871
+ "difficulty": ex["difficulty"],
872
+ "title": ex["title"].get(language, ex["title"]["en"]),
873
+ "target_text": ex["target_text"].get(language, ex["target_text"]["en"]),
874
+ "instructions": ex["instructions"].get(language, ex["instructions"]["en"]),
875
+ "image_url": ex.get("image_url"),
876
+ "audio_url": ex.get("audio_url"),
877
+ "phoneme_focus": ex.get("phoneme_focus", [])
878
+ })
879
+ return exercises
880
+
881
+
882
+ def get_exercises_by_category(category: str, language: str = "en") -> List[Dict]:
883
+ """Return exercises filtered by category."""
884
+ all_exercises = get_all_exercises(language)
885
+ return [ex for ex in all_exercises if ex["category"] == category]
886
+
887
+
888
+ def get_exercises_by_subcategory(subcategory: str, language: str = "en") -> List[Dict]:
889
+ """Return exercises filtered by subcategory."""
890
+ all_exercises = get_all_exercises(language)
891
+ return [ex for ex in all_exercises if ex["subcategory"] == subcategory]
892
+
893
+
894
+ def get_exercises_by_difficulty(difficulty: str, language: str = "en") -> List[Dict]:
895
+ """Return exercises filtered by difficulty."""
896
+ all_exercises = get_all_exercises(language)
897
+ return [ex for ex in all_exercises if ex["difficulty"] == difficulty]
898
+
899
+
900
+ def get_exercise_by_id(exercise_id: str, language: str = "en") -> Optional[Dict]:
901
+ """Return a specific exercise by ID."""
902
+ all_exercises = get_all_exercises(language)
903
+ for ex in all_exercises:
904
+ if ex["id"] == exercise_id:
905
+ return ex
906
+ return None
api/endpoints/v1/routers/therapy.py CHANGED
@@ -34,6 +34,16 @@ from api.endpoints.v1.processing.pronunciation_analysis import (
34
  AIFeedback
35
  )
36
  from api.endpoints.v1.processing.ai_feedback import get_ai_feedback_generator
 
 
 
 
 
 
 
 
 
 
37
 
38
  router = APIRouter()
39
 
@@ -312,77 +322,63 @@ async def analyze_pronunciation_endpoint(
312
  # Demo/Test Endpoints (no auth required)
313
  # ============================================================================
314
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  @router.get("/demo/exercises", tags=["therapy-demo"])
316
- async def demo_list_exercises():
317
- """[DEMO] List exercises without auth - returns actual practice exercises."""
318
- return {
319
- "exercises": [
320
- {
321
- "id": "ex-001",
322
- "title": "Simple Greetings",
323
- "category": "repeat_after_me",
324
- "difficulty": "easy",
325
- "target_text": "Hello, how are you today?",
326
- "instructions": "Listen carefully, then repeat the greeting clearly and naturally."
327
- },
328
- {
329
- "id": "ex-002",
330
- "title": "S Sound Practice",
331
- "category": "minimal_pairs",
332
- "difficulty": "medium",
333
- "target_text": "She sells seashells by the seashore",
334
- "instructions": "Focus on the 'S' and 'SH' sounds. Speak slowly at first."
335
- },
336
- {
337
- "id": "ex-003",
338
- "title": "R Sound Challenge",
339
- "category": "tongue_twisters",
340
- "difficulty": "hard",
341
- "target_text": "Red lorry, yellow lorry",
342
- "instructions": "Practice the 'R' and 'L' sounds. Start slow, then speed up."
343
- },
344
- {
345
- "id": "ex-004",
346
- "title": "Daily Introduction",
347
- "category": "repeat_after_me",
348
- "difficulty": "easy",
349
- "target_text": "My name is... and I am learning to speak clearly.",
350
- "instructions": "Replace '...' with your name. Speak with confidence!"
351
- },
352
- {
353
- "id": "ex-005",
354
- "title": "TH Sound Practice",
355
- "category": "minimal_pairs",
356
- "difficulty": "medium",
357
- "target_text": "Think through these three things thoroughly",
358
- "instructions": "Place your tongue between your teeth for the 'TH' sound."
359
- },
360
- {
361
- "id": "ex-006",
362
- "title": "Peter Piper",
363
- "category": "tongue_twisters",
364
- "difficulty": "hard",
365
- "target_text": "Peter Piper picked a peck of pickled peppers",
366
- "instructions": "Focus on the 'P' sounds. Keep your lips together firmly."
367
- },
368
- {
369
- "id": "ex-007",
370
- "title": "Counting Practice",
371
- "category": "repeat_after_me",
372
- "difficulty": "easy",
373
- "target_text": "One, two, three, four, five",
374
- "instructions": "Count clearly and pause briefly between each number."
375
- },
376
- {
377
- "id": "ex-008",
378
- "title": "W vs V Sounds",
379
- "category": "minimal_pairs",
380
- "difficulty": "medium",
381
- "target_text": "Very well, we will wait",
382
- "instructions": "Notice the difference: 'V' uses teeth on lip, 'W' uses rounded lips."
383
- }
384
- ]
385
- }
386
 
387
 
388
  @router.post("/demo/feedback", tags=["therapy-demo"])
 
34
  AIFeedback
35
  )
36
  from api.endpoints.v1.processing.ai_feedback import get_ai_feedback_generator
37
+ from api.data.exercises import (
38
+ get_all_categories,
39
+ get_subcategory_info,
40
+ get_all_exercises,
41
+ get_exercises_by_category,
42
+ get_exercises_by_subcategory,
43
+ get_exercises_by_difficulty,
44
+ get_exercise_by_id,
45
+ SUBCATEGORIES
46
+ )
47
 
48
  router = APIRouter()
49
 
 
322
  # Demo/Test Endpoints (no auth required)
323
  # ============================================================================
324
 
325
+ @router.get("/demo/categories", tags=["therapy-demo"])
326
+ async def demo_list_categories(
327
+ language: str = Query("en", description="Language: en or fr")
328
+ ):
329
+ """[DEMO] List all exercise categories."""
330
+ categories = get_all_categories()
331
+ result = []
332
+ for cat in categories:
333
+ subcats = []
334
+ for sub_id in cat["subcategories"]:
335
+ sub_info = get_subcategory_info(sub_id)
336
+ if sub_info:
337
+ subcats.append({
338
+ "id": sub_id,
339
+ "name": sub_info["name"].get(language, sub_info["name"]["en"]),
340
+ "description": sub_info["description"].get(language, sub_info["description"]["en"])
341
+ })
342
+ result.append({
343
+ "id": cat["id"],
344
+ "name": cat["name"].get(language, cat["name"]["en"]),
345
+ "description": cat["description"].get(language, cat["description"]["en"]),
346
+ "icon": cat["icon"],
347
+ "subcategories": subcats
348
+ })
349
+ return {"categories": result}
350
+
351
+
352
  @router.get("/demo/exercises", tags=["therapy-demo"])
353
+ async def demo_list_exercises(
354
+ language: str = Query("en", description="Language: en or fr"),
355
+ category: Optional[str] = Query(None, description="Filter by category"),
356
+ subcategory: Optional[str] = Query(None, description="Filter by subcategory"),
357
+ difficulty: Optional[str] = Query(None, description="Filter by difficulty: easy, medium, hard")
358
+ ):
359
+ """[DEMO] List exercises with optional filters."""
360
+ if subcategory:
361
+ exercises = get_exercises_by_subcategory(subcategory, language)
362
+ elif category:
363
+ exercises = get_exercises_by_category(category, language)
364
+ elif difficulty:
365
+ exercises = get_exercises_by_difficulty(difficulty, language)
366
+ else:
367
+ exercises = get_all_exercises(language)
368
+
369
+ return {"exercises": exercises, "total": len(exercises)}
370
+
371
+
372
+ @router.get("/demo/exercises/{exercise_id}", tags=["therapy-demo"])
373
+ async def demo_get_exercise(
374
+ exercise_id: str,
375
+ language: str = Query("en", description="Language: en or fr")
376
+ ):
377
+ """[DEMO] Get a specific exercise by ID."""
378
+ exercise = get_exercise_by_id(exercise_id, language)
379
+ if not exercise:
380
+ raise HTTPException(status_code=404, detail="Exercise not found")
381
+ return exercise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
 
383
 
384
  @router.post("/demo/feedback", tags=["therapy-demo"])