clementBE commited on
Commit
3896ddf
·
verified ·
1 Parent(s): 0726285

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -56
app.py CHANGED
@@ -35,36 +35,23 @@ SOCIAL_MEDIA_CHOICES = [
35
  "Telegram", "Mastodon", "Discord", "Twitter", "Reddit", "messagerie de jeu en ligne",
36
  "autre : préciser", "je ne sais pas"
37
  ]
38
- # CHOICES FOR INFO 7
39
  ANXIETY_MANIFESTATION_CHOICES = [
40
- "J’ai tendance à l’ignorer",
41
- "Je cherche à agir par moi-même",
42
- "Je lis des livres et je me documente sur le sujet",
43
- "J’ai un journal intime",
44
- "Je discute avec mes amis / parents",
45
- "Je pose des questions à mes enseignants",
46
- "Je rejoins une association ou je cherche une activité collective",
47
- "Je consulte un spécialiste de santé",
48
- "Je fais des recherches sur internet",
49
- "J’interroge / je confie à un chatbot",
50
  "Je rejoins une communauté spécialisée sur les réseaux sociaux"
51
  ]
52
 
53
  # --- Language Definitions (French Only) ---
54
  LANG_FR = {
55
- # GLOBAL
56
  "TITLE": "Questionnaire : Pratiques culturelles et Engagement Citoyen",
57
  "INTRO_TEXT": "Bonjour, je suis étudiant.e en **master de communication** et j’effectue une enquête sur les **pratiques culturelles** et les **formes d’engagements citoyen** dans le cadre de mon stage.",
58
  "ENQUETEUR_LABEL": "Identifiant de l'enquêteur",
59
- # TABS
60
- "TAB_1_TITLE": "1. Contact & Lieux Fréquentés",
61
- "TAB_2_TITLE": "2. Engagement Citoyen",
62
- "TAB_3_TITLE": "3. Consommation d’actualités",
63
- "TAB_4_TITLE": "4. Pratiques culturelles et usages numériques",
64
- "TAB_5_TITLE": "5. Profil Démographique",
65
- "TAB_6_TITLE": "6. Questions ouvertes",
66
  "TAB_7_TITLE": "7. Soumission et Téléchargement",
67
- # TAB 1 - Contact & Lieux
68
  "APPROACH_LABEL": "APPROACH - Accepterais-tu de répondre à un questionnaire ? (10 min)",
69
  "REFUSAL_REASON_LABEL": "ANSWER_NO1 (si APPROACH=non) - Pourquoi ?",
70
  "CONTACT_LATER_LABEL": "ANSWER_NO2 (si APPROACH=non) - On peut prendre rendez-vous plus tard ?",
@@ -79,29 +66,25 @@ LANG_FR = {
79
  "SPACE_DOMAIN_LABEL": "Peux-tu caractériser le domaine dans lequel s’inscrit principalement cette pratique ?",
80
  "SPACE11_LABEL": "SPACE11 - Sais-tu si elle s’appuie sur une communauté en ligne et l’usage d’un média social en ligne ?",
81
  "SPACE2B_OPTIONS": ["Pas le temps", "Trop cher", "Je ne sais pas où aller", "Pas intéressé", "Autre"],
82
- # TAB 2 - Engagement
83
  "INFO1_LABEL": "INFO1 - Toi-même, participes-tu à des pratiques d’engagement citoyen ?",
84
  "ENGAGEMENT_ORGANISATION_LABEL": "ENGAGE2 - Dans quelle(s) organisation(s) es-tu engagé·e ?",
85
  "ENGAGEMENT_DOMAINE_LABEL": "ENGAGE3 - Dans quel domaine ? (Si engagé·e)",
86
  "INFO_ACTIVITES_LABEL": "ENGAGE4 - Participes-tu ou as-tu participé à une ou plusieurs des activités suivantes ?",
87
  "INFO_RESEAUX_SOCIAUX_LABEL": "ENGAGE5 - Utilises-tu les réseaux sociaux pour t’engager dans des causes ?",
88
- # TAB 3 - Actualité
89
  "INFO_FREQUENCE_ACTU_LABEL": "INFO1 - À quelle fréquence te tiens-tu informé·e de l’actualité ?",
90
  "INFO2_LABEL": "INFO2 - Quels sont le ou les thèmes d’actualité que tu as suivis avec le plus d’intérêt ?",
91
  "INFO3_LABEL": "INFO3 - Coche **jusqu’à 3 thèmes** qui te semblent poser des enjeux publics majeurs :",
92
  "OPPOSITE_FEELING_BASE_LABEL": "INFO4 - Quand une personne exprime une opinion opposée à la tienne sur un thème majeur, comment te sens-tu ?",
93
  "INFO_CONTRADICT_OPINION_LABEL": "INFO5 - Quand tu tombes sur un article ou une vidéo qui contredit tes croyances, quelle est ta première réaction ?",
94
  "INFO4_LABEL": "INFO6 - Parmi ces thèmes, lequel ou lesquels génèrent chez toi un sentiment d’anxiété ?",
95
- "INFO5_LABEL": "INFO7 - Comment se manifeste ce sentiment d’anxiété ?", # Updated label
96
  "INFO6_TITLE": "INFO8 - Éléments les plus problématiques pour toi (Échelle 1 'sans importance' à 10 'essentiel')",
97
  "INFO6_ITEMS": ["Logement", "Politique/Gouvernement", "Études/Avenir professionnel", "Crise climatique/Environnement", "Inégalités sociales", "Vie sentimentale/familiale", "Sécurité personnelle", "Estime de soi", "Liberté individuelle"],
98
- # TAB 4 - Culturel & Numérique
99
  "PRAT_CULT1_LABEL": "PRAT_CULT1 - Combien de sorties culturelles environ par mois :",
100
  "PRAT_CULT_PRACTICES_TITLE": "PRAT_CULT2 - Parmi les pratiques suivantes, lesquelles effectues-tu régulièrement ?",
101
  "PRAT_NATURE_LABEL": "PRAT_CULT3 - Pratiques-tu une activité liée à la nature (randonnée, observation, bénévolat environnemental...) ?",
102
  "PLATFORM_TITLE": "DIGITAL1 - Quelles plateformes utilises-tu le plus ? (Heures/jour approx.)",
103
  "PURPOSE_TITLE": "DIGITAL2 - Pour quel(s) usage(s) emploies-tu ces plateformes ?",
104
- # TAB 5 - Démographie
105
  "DEMO_GENDER_LABEL": "DEMO_GENDER - Ton genre :",
106
  "DEMO_AGE_LABEL": "DEMO_AGE - Ta date de naissance (AAAA-MM-JJ)",
107
  "DEMO_LOCATION_COMMUNE_LABEL": "DEMO_LOCATION - Commune de résidence habituelle :",
@@ -114,11 +97,9 @@ LANG_FR = {
114
  "DEMO_SOCIALCAPITAL1_PARENT1_LABEL": "DEMO_SOCIALCAPITAL1 - Activité principale Parent 1 (ou responsable légal) :",
115
  "DEMO_SOCIALCAPITAL1_PARENT2_LABEL": "Activite principale Parent 2 :",
116
  "DEMO_SOCIALCAPITAL2_LABEL": "DEMO_SOCIALCAPITAL2 : Sur combien de personnes (hors famille) peux-tu compter en cas de coup dur ?",
117
- # TAB 6 - Questions ouvertes
118
  "OPEN_NON_INSTITUTIONNEL_LABEL": "OEQ1 - Qu’est-ce qu’un lieu culturel non institutionnel selon toi ?",
119
  "OPEN_ALTERNATIVES_LABEL": "OEQ2 - Comment définirais-tu le terme “alternatif” dans ce cas ? (5 mots max.)",
120
  "OPEN_MOTIVATIONS_LABEL": "OEQ3 - Si tu es engagé.e, quelles en sont les motivations principales ?",
121
- # BUTTONS
122
  "SUBMIT_BUTTON": "Soumettre le Questionnaire",
123
  "RESET_BUTTON": "Recommencer",
124
  }
@@ -126,15 +107,22 @@ LANG_FR = {
126
  LANG = LANG_FR
127
 
128
  # =================================================================
129
- # COMPONENT DEFINITIONS (All components defined here, outside gr.Blocks)
130
  # =================================================================
131
 
132
- # Hidden State to pass the language code ('FR')
133
- lang_state = gr.State("FR")
134
 
135
  # === Global ===
136
  enqueteur_id = gr.Textbox(label=LANG["ENQUETEUR_LABEL"], placeholder="Entrez votre identifiant")
137
 
 
 
 
 
 
 
 
138
  # === TAB 1 Components ===
139
  approach_answer = gr.Radio(label=LANG["APPROACH_LABEL"], choices=["Oui", "Non"])
140
  refusal_reason = gr.CheckboxGroup(label=LANG["REFUSAL_REASON_LABEL"], choices=["Pas le temps", "Cela ne m’intéresse pas", "Autre"], visible=False)
@@ -153,7 +141,6 @@ space4 = gr.Radio(label=LANG["SPACE4_LABEL"], choices=["Oui", "Non"])
153
  space5 = gr.Textbox(label=LANG["SPACE5_LABEL"], visible=False)
154
  space6 = gr.Radio(label=LANG["SPACE6_LABEL"], choices=["Oui", "Non", "Ne sait pas"])
155
 
156
- # SPACE 7/DOMAIN/SPACE 11
157
  space7_1 = gr.Textbox(label="Pratique 1", placeholder="Décrivez la pratique d'engagement 1", visible=False)
158
  space7_2 = gr.Textbox(label="Pratique 2", placeholder="Décrivez la pratique d'engagement 2", visible=False)
159
  space7_3 = gr.Textbox(label="Pratique 3", placeholder="Décrivez la pratique d'engagement 3", visible=False)
@@ -164,14 +151,14 @@ space_domain_3 = gr.Radio(label=f"Pratique 3 - {LANG['SPACE_DOMAIN_LABEL']}", ch
164
 
165
  space11 = gr.CheckboxGroup(label=LANG["SPACE11_LABEL"], choices=SOCIAL_MEDIA_CHOICES)
166
 
167
- # === TAB 2 Components (5 components) ===
168
  info1 = gr.Radio(label=LANG["INFO1_LABEL"], choices=["Oui", "Non", "De temps en temps"])
169
  engagement_organisation = gr.Textbox(label=LANG["ENGAGEMENT_ORGANISATION_LABEL"], placeholder="Nom de l'association, collectif...")
170
  engagement_domaine = gr.Textbox(label=LANG["ENGAGEMENT_DOMAINE_LABEL"], placeholder="Domaine de l'engagement...")
171
  info_activites = gr.CheckboxGroup(label=LANG["INFO_ACTIVITES_LABEL"], choices=["Participer à une manifestation/grève", "Contacter un élu", "Signer une pétition en ligne", "Boycotter un produit/marque", "Faire un don à une association", "Autre"], interactive=True)
172
  info_reseaux_sociaux = gr.Radio(label=LANG["INFO_RESEAUX_SOCIAUX_LABEL"], choices=["Oui", "Non", "Parfois"])
173
 
174
- # === TAB 3 Components (16 components) ===
175
  info_frequence_actu = gr.Radio(label=LANG["INFO_FREQUENCE_ACTU_LABEL"], choices=["Plusieurs fois par jour", "Une fois par jour", "Quelques fois par semaine", "Rarement", "Jamais"])
176
  info2 = gr.CheckboxGroup(label=LANG["INFO2_LABEL"], choices=THEMES_ACTU_FR)
177
  info3 = gr.CheckboxGroup(label=LANG["INFO3_LABEL"], choices=THEMES_ACTU_FR)
@@ -186,11 +173,10 @@ for item in LANG["INFO6_ITEMS"]:
186
  clean_id = item.split('/')[0].lower().replace(' ', '_').replace('é', 'e').replace('è', 'e')
187
  info6_sliders[clean_id] = gr.Slider(label=item, minimum=1, maximum=10, step=1)
188
  info6_slider_components = list(info6_sliders.values())
189
- # Explicitly define variables
190
  info6_logement, info6_politique, info6_etudes, info6_climat, info6_sociales, info6_sentimentale, info6_securite, info6_estime, info6_liberte = info6_slider_components
191
 
192
 
193
- # === TAB 4 Components (30 components) ===
194
  prat_cult1 = gr.Number(label=LANG["PRAT_CULT1_LABEL"], minimum=0, maximum=100, step=1)
195
 
196
  # Cultural Frequency Radios (10 components)
@@ -220,7 +206,7 @@ purpose_components.append(gr.Radio(label=f"Fréquence pour: {PURPOSE_CHOICES[-1]
220
  purpose_social, purpose_professionnel, purpose_autre_freq) = purpose_components
221
 
222
 
223
- # === TAB 5 Components (12 components) ===
224
  demo_gender = gr.Radio(label=LANG["DEMO_GENDER_LABEL"], choices=["Homme", "Femme", "Non-binaire", "Préfère ne pas dire"])
225
  demo_age = gr.Textbox(label=LANG["DEMO_AGE_LABEL"], placeholder="ex: 1995-05-20")
226
  demo_location_commune = gr.Textbox(label=LANG["DEMO_LOCATION_COMMUNE_LABEL"], placeholder="Nom de la commune")
@@ -234,17 +220,11 @@ demo_socialcapital1_parent1 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT1_L
234
  demo_socialcapital1_parent2 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT2_LABEL"], choices=INSEE_CHOICES)
235
  demo_socialcapital2 = gr.Number(label=LANG["DEMO_SOCIALCAPITAL2_LABEL"], minimum=0, step=1)
236
 
237
- # === TAB 6 Components (3 components) ===
238
  open_non_institutionnel = gr.Textbox(label=LANG["OPEN_NON_INSTITUTIONNEL_LABEL"], lines=3)
239
  open_alternatives = gr.Textbox(label=LANG["OPEN_ALTERNATIVES_LABEL"], placeholder="5 mots maximum")
240
  open_motivations = gr.Textbox(label=LANG["OPEN_MOTIVATIONS_LABEL"], lines=3)
241
 
242
- # === TAB 7 Components (Submission/Output - MOVED HERE FOR STABILITY) ===
243
- submit_btn = gr.Button(LANG["SUBMIT_BUTTON"], variant="primary")
244
- reset_btn = gr.Button(LANG["RESET_BUTTON"])
245
- output_status = gr.Markdown("---")
246
- output_message = gr.File(label="Télécharger les Données", file_types=['.csv'], visible=False, elem_id="output_message")
247
-
248
 
249
  # Helper function to generate robust column names
250
  def get_column_names(data_components: list) -> list:
@@ -274,7 +254,6 @@ def get_column_names(data_components: list) -> list:
274
  # =================================================================
275
 
276
  def update_visibility_approach(approach):
277
- """Handles visibility for refusal questions based on APPROACH."""
278
  updates = {}
279
  is_non = approach == "Non"
280
  updates[refusal_reason] = gr.update(visible=is_non)
@@ -286,13 +265,11 @@ def update_visibility_approach(approach):
286
  return updates[refusal_reason], updates[contact_later]
287
 
288
  def update_visibility_refusal(reasons):
289
- """Handles visibility for 'other' reason text box."""
290
  if reasons and "Autre" in reasons:
291
  return gr.update(visible=True)
292
  return gr.update(visible=False)
293
 
294
  def update_visibility_space1(frequency):
295
- """Handles visibility for SPACE2a (citation) and SPACE2b (refusal reason)."""
296
  is_frequent = frequency in ["Rarement", "Parfois", "Souvent"]
297
  is_never = frequency == "Jamais"
298
  return {
@@ -306,11 +283,9 @@ def update_visibility_space1(frequency):
306
  }
307
 
308
  def update_visibility_space4(follow_community):
309
- """Handles visibility for SPACE5 (community URL/name)."""
310
  return gr.update(visible=follow_community == "Oui")
311
 
312
  def update_visibility_space6(knows_practices):
313
- """Handles visibility for SPACE7 (citing practices) and the subsequent domain/media questions."""
314
  is_yes = knows_practices == "Oui"
315
  outputs = {
316
  space7_1: gr.update(visible=is_yes),
@@ -325,7 +300,6 @@ def update_visibility_space6(knows_practices):
325
  return outputs
326
 
327
  def update_domain_visibility(practice_text):
328
- """Handles visibility for the domain selection based on whether a practice has been cited."""
329
  is_cited = bool(practice_text.strip())
330
  return gr.update(visible=is_cited)
331
 
@@ -342,17 +316,19 @@ def process_survey(*all_inputs: Any) -> Tuple[str, gr.File]:
342
  error_msg = f"Erreur critique: Le nombre d'entrées reçues est incorrect ({len(all_inputs_list)} au lieu de {EXPECTED_TOTAL_COUNT}). Veuillez contacter le développeur."
343
  return error_msg, gr.update(visible=False, value=None)
344
 
345
- language = all_inputs_list[-1]
346
- data_values = all_inputs_list[:-1]
 
347
 
348
  try:
349
  column_names = get_column_names(DATA_INPUT_COMPONENTS)
350
- column_names.append("DEMO_LANGUAGE")
351
 
352
- data_row = data_values + [language]
353
 
354
  df = pd.DataFrame([data_row], columns=column_names)
355
 
 
356
  temp_filename = f"/tmp/survey_response_{int(time.time())}.csv"
357
  os.makedirs(os.path.dirname(temp_filename), exist_ok=True)
358
  df.to_csv(temp_filename, index=False, encoding='utf-8')
@@ -367,7 +343,7 @@ def process_survey(*all_inputs: Any) -> Tuple[str, gr.File]:
367
 
368
 
369
  # =================================================================
370
- # DATA COMPONENT FINAL ASSEMBLY
371
  # =================================================================
372
  DATA_INPUT_COMPONENTS = [
373
  # Global (1)
@@ -409,7 +385,8 @@ DATA_INPUT_COMPONENTS = [
409
  if len(DATA_INPUT_COMPONENTS) != EXPECTED_DATA_COUNT:
410
  raise RuntimeError(f"Internal component count mismatch. Expected {EXPECTED_DATA_COUNT}, got {len(DATA_INPUT_COMPONENTS)}. Please verify the DATA_INPUT_COMPONENTS list definition.")
411
 
412
- SUBMIT_INPUT_COMPONENTS = DATA_INPUT_COMPONENTS + [lang_state]
 
413
 
414
 
415
  # =================================================================
@@ -417,6 +394,7 @@ SUBMIT_INPUT_COMPONENTS = DATA_INPUT_COMPONENTS + [lang_state]
417
  # =================================================================
418
 
419
  with gr.Blocks(title=LANG["TITLE"], css=".gradio-container { max-width: 1200px; }") as demo:
 
420
  gr.Markdown(f"## {LANG['TITLE']}")
421
  gr.Markdown(LANG["INTRO_TEXT"])
422
 
@@ -601,7 +579,7 @@ with gr.Blocks(title=LANG["TITLE"], css=".gradio-container { max-width: 1200px;
601
  open_motivations.render()
602
 
603
  # =================================================================
604
- # TAB 7: Submission and Download (FIXED)
605
  # =================================================================
606
  with gr.Tab(LANG["TAB_7_TITLE"]):
607
  gr.Markdown("### Finalisation du Questionnaire")
@@ -616,6 +594,7 @@ with gr.Blocks(title=LANG["TITLE"], css=".gradio-container { max-width: 1200px;
616
 
617
 
618
  # Submit action
 
619
  submit_btn.click(
620
  fn=process_survey,
621
  inputs=SUBMIT_INPUT_COMPONENTS,
 
35
  "Telegram", "Mastodon", "Discord", "Twitter", "Reddit", "messagerie de jeu en ligne",
36
  "autre : préciser", "je ne sais pas"
37
  ]
 
38
  ANXIETY_MANIFESTATION_CHOICES = [
39
+ "J’ai tendance à l’ignorer", "Je cherche à agir par moi-même", "Je lis des livres et je me documente sur le sujet",
40
+ "J’ai un journal intime", "Je discute avec mes amis / parents", "Je pose des questions à mes enseignants",
41
+ "Je rejoins une association ou je cherche une activité collective", "Je consulte un spécialiste de santé",
42
+ "Je fais des recherches sur internet", "J’interroge / je confie à un chatbot",
 
 
 
 
 
 
43
  "Je rejoins une communauté spécialisée sur les réseaux sociaux"
44
  ]
45
 
46
  # --- Language Definitions (French Only) ---
47
  LANG_FR = {
 
48
  "TITLE": "Questionnaire : Pratiques culturelles et Engagement Citoyen",
49
  "INTRO_TEXT": "Bonjour, je suis étudiant.e en **master de communication** et j’effectue une enquête sur les **pratiques culturelles** et les **formes d’engagements citoyen** dans le cadre de mon stage.",
50
  "ENQUETEUR_LABEL": "Identifiant de l'enquêteur",
51
+ "TAB_1_TITLE": "1. Contact & Lieux Fréquentés", "TAB_2_TITLE": "2. Engagement Citoyen",
52
+ "TAB_3_TITLE": "3. Consommation d’actualités", "TAB_4_TITLE": "4. Pratiques culturelles et usages numériques",
53
+ "TAB_5_TITLE": "5. Profil Démographique", "TAB_6_TITLE": "6. Questions ouvertes",
 
 
 
 
54
  "TAB_7_TITLE": "7. Soumission et Téléchargement",
 
55
  "APPROACH_LABEL": "APPROACH - Accepterais-tu de répondre à un questionnaire ? (10 min)",
56
  "REFUSAL_REASON_LABEL": "ANSWER_NO1 (si APPROACH=non) - Pourquoi ?",
57
  "CONTACT_LATER_LABEL": "ANSWER_NO2 (si APPROACH=non) - On peut prendre rendez-vous plus tard ?",
 
66
  "SPACE_DOMAIN_LABEL": "Peux-tu caractériser le domaine dans lequel s’inscrit principalement cette pratique ?",
67
  "SPACE11_LABEL": "SPACE11 - Sais-tu si elle s’appuie sur une communauté en ligne et l’usage d’un média social en ligne ?",
68
  "SPACE2B_OPTIONS": ["Pas le temps", "Trop cher", "Je ne sais pas où aller", "Pas intéressé", "Autre"],
 
69
  "INFO1_LABEL": "INFO1 - Toi-même, participes-tu à des pratiques d’engagement citoyen ?",
70
  "ENGAGEMENT_ORGANISATION_LABEL": "ENGAGE2 - Dans quelle(s) organisation(s) es-tu engagé·e ?",
71
  "ENGAGEMENT_DOMAINE_LABEL": "ENGAGE3 - Dans quel domaine ? (Si engagé·e)",
72
  "INFO_ACTIVITES_LABEL": "ENGAGE4 - Participes-tu ou as-tu participé à une ou plusieurs des activités suivantes ?",
73
  "INFO_RESEAUX_SOCIAUX_LABEL": "ENGAGE5 - Utilises-tu les réseaux sociaux pour t’engager dans des causes ?",
 
74
  "INFO_FREQUENCE_ACTU_LABEL": "INFO1 - À quelle fréquence te tiens-tu informé·e de l’actualité ?",
75
  "INFO2_LABEL": "INFO2 - Quels sont le ou les thèmes d’actualité que tu as suivis avec le plus d’intérêt ?",
76
  "INFO3_LABEL": "INFO3 - Coche **jusqu’à 3 thèmes** qui te semblent poser des enjeux publics majeurs :",
77
  "OPPOSITE_FEELING_BASE_LABEL": "INFO4 - Quand une personne exprime une opinion opposée à la tienne sur un thème majeur, comment te sens-tu ?",
78
  "INFO_CONTRADICT_OPINION_LABEL": "INFO5 - Quand tu tombes sur un article ou une vidéo qui contredit tes croyances, quelle est ta première réaction ?",
79
  "INFO4_LABEL": "INFO6 - Parmi ces thèmes, lequel ou lesquels génèrent chez toi un sentiment d’anxiété ?",
80
+ "INFO5_LABEL": "INFO7 - Comment se manifeste ce sentiment d’anxiété ?",
81
  "INFO6_TITLE": "INFO8 - Éléments les plus problématiques pour toi (Échelle 1 'sans importance' à 10 'essentiel')",
82
  "INFO6_ITEMS": ["Logement", "Politique/Gouvernement", "Études/Avenir professionnel", "Crise climatique/Environnement", "Inégalités sociales", "Vie sentimentale/familiale", "Sécurité personnelle", "Estime de soi", "Liberté individuelle"],
 
83
  "PRAT_CULT1_LABEL": "PRAT_CULT1 - Combien de sorties culturelles environ par mois :",
84
  "PRAT_CULT_PRACTICES_TITLE": "PRAT_CULT2 - Parmi les pratiques suivantes, lesquelles effectues-tu régulièrement ?",
85
  "PRAT_NATURE_LABEL": "PRAT_CULT3 - Pratiques-tu une activité liée à la nature (randonnée, observation, bénévolat environnemental...) ?",
86
  "PLATFORM_TITLE": "DIGITAL1 - Quelles plateformes utilises-tu le plus ? (Heures/jour approx.)",
87
  "PURPOSE_TITLE": "DIGITAL2 - Pour quel(s) usage(s) emploies-tu ces plateformes ?",
 
88
  "DEMO_GENDER_LABEL": "DEMO_GENDER - Ton genre :",
89
  "DEMO_AGE_LABEL": "DEMO_AGE - Ta date de naissance (AAAA-MM-JJ)",
90
  "DEMO_LOCATION_COMMUNE_LABEL": "DEMO_LOCATION - Commune de résidence habituelle :",
 
97
  "DEMO_SOCIALCAPITAL1_PARENT1_LABEL": "DEMO_SOCIALCAPITAL1 - Activité principale Parent 1 (ou responsable légal) :",
98
  "DEMO_SOCIALCAPITAL1_PARENT2_LABEL": "Activite principale Parent 2 :",
99
  "DEMO_SOCIALCAPITAL2_LABEL": "DEMO_SOCIALCAPITAL2 : Sur combien de personnes (hors famille) peux-tu compter en cas de coup dur ?",
 
100
  "OPEN_NON_INSTITUTIONNEL_LABEL": "OEQ1 - Qu’est-ce qu’un lieu culturel non institutionnel selon toi ?",
101
  "OPEN_ALTERNATIVES_LABEL": "OEQ2 - Comment définirais-tu le terme “alternatif” dans ce cas ? (5 mots max.)",
102
  "OPEN_MOTIVATIONS_LABEL": "OEQ3 - Si tu es engagé.e, quelles en sont les motivations principales ?",
 
103
  "SUBMIT_BUTTON": "Soumettre le Questionnaire",
104
  "RESET_BUTTON": "Recommencer",
105
  }
 
107
  LANG = LANG_FR
108
 
109
  # =================================================================
110
+ # COMPONENT DEFINITIONS
111
  # =================================================================
112
 
113
+ # Hidden State (Most likely to be ID 0)
114
+ lang_state = gr.State("FR", elem_id="lang_state_comp") # Added elem_id for stability
115
 
116
  # === Global ===
117
  enqueteur_id = gr.Textbox(label=LANG["ENQUETEUR_LABEL"], placeholder="Entrez votre identifiant")
118
 
119
+ # === Submission/Output Components ===
120
+ submit_btn = gr.Button(LANG["SUBMIT_BUTTON"], variant="primary")
121
+ reset_btn = gr.Button(LANG["RESET_BUTTON"])
122
+ output_status = gr.Markdown("---")
123
+ output_message = gr.File(label="Télécharger les Données", file_types=['.csv'], visible=False, elem_id="output_message")
124
+
125
+
126
  # === TAB 1 Components ===
127
  approach_answer = gr.Radio(label=LANG["APPROACH_LABEL"], choices=["Oui", "Non"])
128
  refusal_reason = gr.CheckboxGroup(label=LANG["REFUSAL_REASON_LABEL"], choices=["Pas le temps", "Cela ne m’intéresse pas", "Autre"], visible=False)
 
141
  space5 = gr.Textbox(label=LANG["SPACE5_LABEL"], visible=False)
142
  space6 = gr.Radio(label=LANG["SPACE6_LABEL"], choices=["Oui", "Non", "Ne sait pas"])
143
 
 
144
  space7_1 = gr.Textbox(label="Pratique 1", placeholder="Décrivez la pratique d'engagement 1", visible=False)
145
  space7_2 = gr.Textbox(label="Pratique 2", placeholder="Décrivez la pratique d'engagement 2", visible=False)
146
  space7_3 = gr.Textbox(label="Pratique 3", placeholder="Décrivez la pratique d'engagement 3", visible=False)
 
151
 
152
  space11 = gr.CheckboxGroup(label=LANG["SPACE11_LABEL"], choices=SOCIAL_MEDIA_CHOICES)
153
 
154
+ # === TAB 2 Components ===
155
  info1 = gr.Radio(label=LANG["INFO1_LABEL"], choices=["Oui", "Non", "De temps en temps"])
156
  engagement_organisation = gr.Textbox(label=LANG["ENGAGEMENT_ORGANISATION_LABEL"], placeholder="Nom de l'association, collectif...")
157
  engagement_domaine = gr.Textbox(label=LANG["ENGAGEMENT_DOMAINE_LABEL"], placeholder="Domaine de l'engagement...")
158
  info_activites = gr.CheckboxGroup(label=LANG["INFO_ACTIVITES_LABEL"], choices=["Participer à une manifestation/grève", "Contacter un élu", "Signer une pétition en ligne", "Boycotter un produit/marque", "Faire un don à une association", "Autre"], interactive=True)
159
  info_reseaux_sociaux = gr.Radio(label=LANG["INFO_RESEAUX_SOCIAUX_LABEL"], choices=["Oui", "Non", "Parfois"])
160
 
161
+ # === TAB 3 Components ===
162
  info_frequence_actu = gr.Radio(label=LANG["INFO_FREQUENCE_ACTU_LABEL"], choices=["Plusieurs fois par jour", "Une fois par jour", "Quelques fois par semaine", "Rarement", "Jamais"])
163
  info2 = gr.CheckboxGroup(label=LANG["INFO2_LABEL"], choices=THEMES_ACTU_FR)
164
  info3 = gr.CheckboxGroup(label=LANG["INFO3_LABEL"], choices=THEMES_ACTU_FR)
 
173
  clean_id = item.split('/')[0].lower().replace(' ', '_').replace('é', 'e').replace('è', 'e')
174
  info6_sliders[clean_id] = gr.Slider(label=item, minimum=1, maximum=10, step=1)
175
  info6_slider_components = list(info6_sliders.values())
 
176
  info6_logement, info6_politique, info6_etudes, info6_climat, info6_sociales, info6_sentimentale, info6_securite, info6_estime, info6_liberte = info6_slider_components
177
 
178
 
179
+ # === TAB 4 Components ===
180
  prat_cult1 = gr.Number(label=LANG["PRAT_CULT1_LABEL"], minimum=0, maximum=100, step=1)
181
 
182
  # Cultural Frequency Radios (10 components)
 
206
  purpose_social, purpose_professionnel, purpose_autre_freq) = purpose_components
207
 
208
 
209
+ # === TAB 5 Components ===
210
  demo_gender = gr.Radio(label=LANG["DEMO_GENDER_LABEL"], choices=["Homme", "Femme", "Non-binaire", "Préfère ne pas dire"])
211
  demo_age = gr.Textbox(label=LANG["DEMO_AGE_LABEL"], placeholder="ex: 1995-05-20")
212
  demo_location_commune = gr.Textbox(label=LANG["DEMO_LOCATION_COMMUNE_LABEL"], placeholder="Nom de la commune")
 
220
  demo_socialcapital1_parent2 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT2_LABEL"], choices=INSEE_CHOICES)
221
  demo_socialcapital2 = gr.Number(label=LANG["DEMO_SOCIALCAPITAL2_LABEL"], minimum=0, step=1)
222
 
223
+ # === TAB 6 Components ===
224
  open_non_institutionnel = gr.Textbox(label=LANG["OPEN_NON_INSTITUTIONNEL_LABEL"], lines=3)
225
  open_alternatives = gr.Textbox(label=LANG["OPEN_ALTERNATIVES_LABEL"], placeholder="5 mots maximum")
226
  open_motivations = gr.Textbox(label=LANG["OPEN_MOTIVATIONS_LABEL"], lines=3)
227
 
 
 
 
 
 
 
228
 
229
  # Helper function to generate robust column names
230
  def get_column_names(data_components: list) -> list:
 
254
  # =================================================================
255
 
256
  def update_visibility_approach(approach):
 
257
  updates = {}
258
  is_non = approach == "Non"
259
  updates[refusal_reason] = gr.update(visible=is_non)
 
265
  return updates[refusal_reason], updates[contact_later]
266
 
267
  def update_visibility_refusal(reasons):
 
268
  if reasons and "Autre" in reasons:
269
  return gr.update(visible=True)
270
  return gr.update(visible=False)
271
 
272
  def update_visibility_space1(frequency):
 
273
  is_frequent = frequency in ["Rarement", "Parfois", "Souvent"]
274
  is_never = frequency == "Jamais"
275
  return {
 
283
  }
284
 
285
  def update_visibility_space4(follow_community):
 
286
  return gr.update(visible=follow_community == "Oui")
287
 
288
  def update_visibility_space6(knows_practices):
 
289
  is_yes = knows_practices == "Oui"
290
  outputs = {
291
  space7_1: gr.update(visible=is_yes),
 
300
  return outputs
301
 
302
  def update_domain_visibility(practice_text):
 
303
  is_cited = bool(practice_text.strip())
304
  return gr.update(visible=is_cited)
305
 
 
316
  error_msg = f"Erreur critique: Le nombre d'entrées reçues est incorrect ({len(all_inputs_list)} au lieu de {EXPECTED_TOTAL_COUNT}). Veuillez contacter le développeur."
317
  return error_msg, gr.update(visible=False, value=None)
318
 
319
+ # CRITICAL FIX: Language state is now the first input (index 0)
320
+ language = all_inputs_list[0]
321
+ data_values = all_inputs_list[1:]
322
 
323
  try:
324
  column_names = get_column_names(DATA_INPUT_COMPONENTS)
325
+ column_names.insert(0, "DEMO_LANGUAGE") # Insert language as the first column name
326
 
327
+ data_row = [language] + data_values
328
 
329
  df = pd.DataFrame([data_row], columns=column_names)
330
 
331
+ # Create a temporary file name
332
  temp_filename = f"/tmp/survey_response_{int(time.time())}.csv"
333
  os.makedirs(os.path.dirname(temp_filename), exist_ok=True)
334
  df.to_csv(temp_filename, index=False, encoding='utf-8')
 
343
 
344
 
345
  # =================================================================
346
+ # DATA COMPONENT FINAL ASSEMBLY
347
  # =================================================================
348
  DATA_INPUT_COMPONENTS = [
349
  # Global (1)
 
385
  if len(DATA_INPUT_COMPONENTS) != EXPECTED_DATA_COUNT:
386
  raise RuntimeError(f"Internal component count mismatch. Expected {EXPECTED_DATA_COUNT}, got {len(DATA_INPUT_COMPONENTS)}. Please verify the DATA_INPUT_COMPONENTS list definition.")
387
 
388
+ # CRITICAL FIX: Put lang_state first to align with potential ID=0 mapping
389
+ SUBMIT_INPUT_COMPONENTS = [lang_state] + DATA_INPUT_COMPONENTS
390
 
391
 
392
  # =================================================================
 
394
  # =================================================================
395
 
396
  with gr.Blocks(title=LANG["TITLE"], css=".gradio-container { max-width: 1200px; }") as demo:
397
+
398
  gr.Markdown(f"## {LANG['TITLE']}")
399
  gr.Markdown(LANG["INTRO_TEXT"])
400
 
 
579
  open_motivations.render()
580
 
581
  # =================================================================
582
+ # TAB 7: Submission and Download
583
  # =================================================================
584
  with gr.Tab(LANG["TAB_7_TITLE"]):
585
  gr.Markdown("### Finalisation du Questionnaire")
 
594
 
595
 
596
  # Submit action
597
+ # Inputs list starts with lang_state
598
  submit_btn.click(
599
  fn=process_survey,
600
  inputs=SUBMIT_INPUT_COMPONENTS,