Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,12 +6,12 @@ from typing import List, Union, Dict, Any, Tuple
|
|
| 6 |
from datetime import datetime
|
| 7 |
|
| 8 |
# =================================================================
|
| 9 |
-
# CONFIGURATION AND LANGUAGE DEFINITIONS
|
| 10 |
# =================================================================
|
| 11 |
|
| 12 |
-
#
|
| 13 |
-
EXPECTED_DATA_COUNT =
|
| 14 |
-
EXPECTED_COUNT =
|
| 15 |
|
| 16 |
# --- Shared Choices (French Only) ---
|
| 17 |
FREQ_FR = ["Jamais", "Rarement", "Parfois", "Souvent"]
|
|
@@ -31,13 +31,19 @@ DOMAIN_CHOICES_FR = [
|
|
| 31 |
"Ancrage local (bar associatif, aides aux personnes agées, bibliothèque associative…)",
|
| 32 |
"autre - préciser"
|
| 33 |
]
|
|
|
|
| 34 |
ENGAGE3_CHOICES_FR = [
|
| 35 |
-
"écologie",
|
| 36 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
]
|
| 38 |
SOCIAL_MEDIA_CHOICES = [
|
| 39 |
-
"Instagram", "YouTube", "TikTok", "Facebook", "WhatsApp", "BlueSky", "Signal",
|
| 40 |
-
"Telegram", "Mastodon", "Discord", "Twitter", "Reddit", "messagerie de jeu en ligne",
|
| 41 |
"autre : préciser", "je ne sais pas"
|
| 42 |
]
|
| 43 |
ANXIETY_MANIFESTATION_CHOICES = [
|
|
@@ -47,35 +53,52 @@ ANXIETY_MANIFESTATION_CHOICES = [
|
|
| 47 |
"Je fais des recherches sur internet", "J’interroge / je confie à un chatbot",
|
| 48 |
"Je rejoins une communauté spécialisée sur les réseaux sociaux"
|
| 49 |
]
|
|
|
|
| 50 |
INFO4_CHOICES = ["Curieux", "Indifférent·e", "Légèrement agacé·e", "Inquiet·ète", "En colère", "Déstabilisé·e"]
|
| 51 |
INFO5_CHOICES = [
|
| 52 |
-
"Je le lis/regarde avec attention.",
|
| 53 |
-
"Je le
|
|
|
|
|
|
|
|
|
|
| 54 |
]
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
]
|
|
|
|
| 58 |
ENGAGE2_CHOICES = [
|
| 59 |
-
"une association",
|
| 60 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
]
|
|
|
|
| 62 |
DEMO_INSCRIPTION_CHOICES = ["L1", "L2", "L3", "M1", "M2", "D", "DU", "autre niveau d’étude/diplôme"]
|
| 63 |
DEMO_DISCIPLINE_CHOICES = [
|
| 64 |
-
"Cinéma", "Communication", "Langues", "LEA", "Lettres", "Médiation", "Musique",
|
| 65 |
"Sciences du langage", "Théâtre", "Traduction", "Autres"
|
| 66 |
]
|
| 67 |
-
|
| 68 |
|
| 69 |
# --- Language Definitions (French Only) ---
|
| 70 |
-
|
| 71 |
-
LANG = {
|
| 72 |
"TITLE": "Questionnaire : Pratiques culturelles et Engagement Citoyen",
|
| 73 |
"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.",
|
| 74 |
-
"ENQUETEUR_LABEL": "Identifiant de l'enquêteur",
|
| 75 |
"TAB_1_TITLE": "1. Contact & Lieux Fréquentés", "TAB_2_TITLE": "2. Engagement Citoyen",
|
| 76 |
"TAB_3_TITLE": "3. Consommation d’actualités", "TAB_4_TITLE": "4. Pratiques culturelles et usages numériques",
|
| 77 |
"TAB_5_TITLE": "5. Profil Démographique", "TAB_6_TITLE": "6. Questions ouvertes",
|
| 78 |
-
"TAB_7_TITLE": "7. Soumission et Téléchargement",
|
| 79 |
"APPROACH_LABEL": "APPROACH - Accepterais-tu de répondre à un questionnaire ? (10 min)",
|
| 80 |
"REFUSAL_REASON_LABEL": "ANSWER_NO1 (si APPROACH=non) - Pourquoi ?",
|
| 81 |
"CONTACT_LATER_LABEL": "ANSWER_NO2 (si APPROACH=non) - On peut prendre rendez-vous plus tard ?",
|
|
@@ -95,9 +118,9 @@ LANG = {
|
|
| 95 |
"SPACE2B_OPTIONS": ["Pas le temps", "Trop cher", "Je ne sais pas où aller", "Pas intéressé", "Autre"],
|
| 96 |
"ENGAGE1_LABEL": "ENGAGE1 - Toi-même, participes-tu à des pratiques d’engagement citoyen ?",
|
| 97 |
"ENGAGEMENT_ORGANISATION_LABEL": "ENGAGE2 - Dans quelle(s) organisation(s) es-tu engagé·e ?",
|
| 98 |
-
"ENGAGE2_AUTRE_DETAIL_LABEL": "ENGAGE2 - Précisez 'autre'
|
| 99 |
"ENGAGEMENT_DOMAINE_LABEL": "ENGAGE3 - Dans quel(s) domaine(s) es-tu engagé·e ?",
|
| 100 |
-
"ENGAGE3_AUTRE_DETAIL_LABEL": "ENGAGE3 - Précisez 'autre'
|
| 101 |
"INFO_ACTIVITES_LABEL": "ENGAGE4 - Participes-tu ou as-tu participé à une ou plusieurs des activités suivantes ?",
|
| 102 |
"INFO_RESEAUX_SOCIAUX_LABEL": "ENGAGE5 - Utilises-tu les réseaux sociaux pour t’engager dans des causes ?",
|
| 103 |
"INFO_FREQUENCE_ACTU_LABEL": "INFO1 - À quelle fréquence te tiens-tu informé·e de l’actualité ?",
|
|
@@ -106,7 +129,7 @@ LANG = {
|
|
| 106 |
"OPPOSITE_FEELING_BASE_LABEL": "INFO4 - Quand une personne exprime une opinion opposée à la tienne sur le thème qui te semble représenter des enjeux majeurs, comment te sens-tu ?",
|
| 107 |
"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 ?",
|
| 108 |
"INFO4_LABEL": "INFO6 - Parmi ces thèmes, lequel ou lesquels génèrent chez toi un sentiment d’anxiété ?",
|
| 109 |
-
"INFO5_LABEL": "INFO7 - Comment se manifeste ce sentiment d’anxiété ?",
|
| 110 |
"INFO6_TITLE": "INFO8 - Éléments les plus problématiques pour toi (Échelle 1 'sans importance' à 10 'essentiel')",
|
| 111 |
"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"],
|
| 112 |
"PRAT_CULT1_LABEL": "PRAT_CULT1 - Combien de sorties culturelles environ par mois :",
|
|
@@ -133,23 +156,26 @@ LANG = {
|
|
| 133 |
"RESET_BUTTON": "Recommencer",
|
| 134 |
}
|
| 135 |
|
|
|
|
| 136 |
|
| 137 |
# =================================================================
|
| 138 |
-
# COMPONENT DEFINITIONS
|
| 139 |
# =================================================================
|
| 140 |
|
| 141 |
# === Global ===
|
| 142 |
enqueteur_id = gr.Textbox(label=LANG["ENQUETEUR_LABEL"], placeholder="Entrez votre identifiant")
|
|
|
|
|
|
|
| 143 |
submit_btn = gr.Button(LANG["SUBMIT_BUTTON"], variant="primary")
|
| 144 |
reset_btn = gr.Button(LANG["RESET_BUTTON"])
|
| 145 |
output_status = gr.Markdown("---")
|
| 146 |
output_message = gr.File(label="Télécharger les Données", file_types=['.csv'], visible=False, elem_id="output_message")
|
| 147 |
|
| 148 |
|
| 149 |
-
# === TAB 1 Components
|
| 150 |
approach_answer = gr.Radio(label=LANG["APPROACH_LABEL"], choices=["Oui", "Non"])
|
| 151 |
refusal_reason = gr.CheckboxGroup(label=LANG["REFUSAL_REASON_LABEL"], choices=["Pas le temps", "Cela ne m’intéresse pas", "Autre"], visible=False)
|
| 152 |
-
refusal_reason_other = gr.Textbox(label="Précisez 'Autre'", visible=False)
|
| 153 |
contact_later = gr.Radio(label=LANG["CONTACT_LATER_LABEL"], choices=["Oui", "Non"], visible=False)
|
| 154 |
|
| 155 |
space1 = gr.Radio(label=LANG["SPACE1_LABEL"], choices=FREQ_FR)
|
|
@@ -170,45 +196,33 @@ space7_1 = gr.Textbox(label="Pratique 1", placeholder="Décrivez la pratique d'e
|
|
| 170 |
space7_2 = gr.Textbox(label="Pratique 2", placeholder="Décrivez la pratique d'engagement 2", visible=False)
|
| 171 |
space7_3 = gr.Textbox(label="Pratique 3", placeholder="Décrivez la pratique d'engagement 3", visible=False)
|
| 172 |
|
|
|
|
| 173 |
space_domain_1 = gr.CheckboxGroup(label=f"Domaines pour Pratique 1", choices=DOMAIN_CHOICES_FR, visible=False)
|
| 174 |
space_domain_2 = gr.CheckboxGroup(label=f"Domaines pour Pratique 2", choices=DOMAIN_CHOICES_FR, visible=False)
|
| 175 |
space_domain_3 = gr.CheckboxGroup(label=f"Domaines pour Pratique 3", choices=DOMAIN_CHOICES_FR, visible=False)
|
| 176 |
-
space_domain_autre_detail = gr.Textbox(label="Précisez le domaine 'autre - préciser'", visible=False) # PRECISION 2
|
| 177 |
|
| 178 |
space11 = gr.CheckboxGroup(label=LANG["SPACE11_LABEL"], choices=SOCIAL_MEDIA_CHOICES)
|
| 179 |
-
space11_autre_detail = gr.Textbox(label="Précisez le média social 'autre : préciser'", visible=False) # PRECISION 3
|
| 180 |
-
|
| 181 |
|
| 182 |
-
# === TAB 2 Components
|
| 183 |
engage1_role = gr.CheckboxGroup(label=LANG["ENGAGE1_LABEL"], choices=ENGAGE1_CHOICES)
|
| 184 |
engage2_type = gr.CheckboxGroup(label=LANG["ENGAGEMENT_ORGANISATION_LABEL"], choices=ENGAGE2_CHOICES)
|
| 185 |
-
engage2_autre_detail = gr.Textbox(label=LANG["ENGAGE2_AUTRE_DETAIL_LABEL"], visible=False)
|
| 186 |
|
|
|
|
| 187 |
engagement_domaine = gr.CheckboxGroup(label=LANG["ENGAGEMENT_DOMAINE_LABEL"], choices=ENGAGE3_CHOICES_FR)
|
| 188 |
-
engage3_autre_detail = gr.Textbox(label=LANG["ENGAGE3_AUTRE_DETAIL_LABEL"], visible=False)
|
| 189 |
-
|
| 190 |
-
info_activites = gr.CheckboxGroup(label=LANG["INFO_ACTIVITES_LABEL"], choices=INFO_ACTIVITES_CHOICES, interactive=True)
|
| 191 |
-
info_activites_autre_detail = gr.Textbox(label="Précisez l'activité 'Autre'", visible=False) # PRECISION 6
|
| 192 |
|
|
|
|
| 193 |
info_reseaux_sociaux = gr.Radio(label=LANG["INFO_RESEAUX_SOCIAUX_LABEL"], choices=["Oui", "Non", "Parfois"])
|
| 194 |
|
| 195 |
-
|
| 196 |
-
# === TAB 3 Components (25 total) ===
|
| 197 |
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"])
|
| 198 |
-
|
| 199 |
info2 = gr.CheckboxGroup(label=LANG["INFO2_LABEL"], choices=THEMES_ACTU_FR)
|
| 200 |
-
info2_autre_detail = gr.Textbox(label="INFO2 - Précisez 'autre/précisez'", visible=False) # PRECISION 7
|
| 201 |
-
|
| 202 |
info3 = gr.CheckboxGroup(label=LANG["INFO3_LABEL"], choices=THEMES_ACTU_FR)
|
| 203 |
-
info3_autre_detail = gr.Textbox(label="INFO3 - Précisez 'autre/précisez'", visible=False) # PRECISION 8
|
| 204 |
-
|
| 205 |
info_opposite_feeling = gr.Radio(label=LANG["OPPOSITE_FEELING_BASE_LABEL"], choices=INFO4_CHOICES)
|
| 206 |
-
|
| 207 |
info_contradict_opinion = gr.Radio(label=LANG["INFO_CONTRADICT_OPINION_LABEL"], choices=INFO5_CHOICES)
|
| 208 |
-
info_contradict_opinion_autre_detail = gr.Textbox(label="INFO5 - Précisez l''autre réaction'", visible=False) # PRECISION 9
|
| 209 |
-
|
| 210 |
info4 = gr.CheckboxGroup(label=LANG["INFO4_LABEL"], choices=THEMES_ACTU_FR)
|
| 211 |
-
info5 = gr.CheckboxGroup(label=LANG["INFO5_LABEL"], choices=ANXIETY_MANIFESTATION_CHOICES)
|
| 212 |
|
| 213 |
# Info 8 Sliders (9 components)
|
| 214 |
info6_sliders = {}
|
|
@@ -216,16 +230,17 @@ for item in LANG["INFO6_ITEMS"]:
|
|
| 216 |
clean_id = item.split('/')[0].lower().replace(' ', '_').replace('é', 'e').replace('è', 'e')
|
| 217 |
info6_sliders[clean_id] = gr.Slider(label=item, minimum=1, maximum=10, step=1)
|
| 218 |
info6_slider_components = list(info6_sliders.values())
|
|
|
|
| 219 |
|
| 220 |
|
| 221 |
-
# === TAB 4 Components
|
| 222 |
prat_cult1 = gr.Number(label=LANG["PRAT_CULT1_LABEL"], minimum=0, maximum=100, step=1)
|
| 223 |
|
| 224 |
# Cultural Frequency Radios (10 components)
|
| 225 |
prat_cult_freq_components = [
|
| 226 |
gr.Radio(label=f"{activity}", choices=FREQ_FR) for activity in ACTIVITY_CHOICES
|
| 227 |
]
|
| 228 |
-
(prat_cult_lecture, prat_cult_cinema, prat_cult_musique, prat_cult_theatre, prat_cult_expositions,
|
| 229 |
prat_cult_sport, prat_cult_voyages, prat_cult_jeux_video, prat_cult_bricolage, prat_cult_reseaux) = prat_cult_freq_components
|
| 230 |
|
| 231 |
prat_nature = gr.Radio(label=LANG["PRAT_NATURE_LABEL"], choices=["Oui", "Non", "Parfois"])
|
|
@@ -234,130 +249,502 @@ prat_nature = gr.Radio(label=LANG["PRAT_NATURE_LABEL"], choices=["Oui", "Non", "
|
|
| 234 |
platform_components = [
|
| 235 |
gr.Number(label=f"Heures/jour pour {platform}", placeholder="0.5, 1, 2...", minimum=0, step=0.5) for platform in PLATFORM_CHOICES
|
| 236 |
]
|
| 237 |
-
(plat_instagram, plat_tiktok, plat_youtube, plat_twitter, plat_facebook,
|
| 238 |
plat_snapchat, plat_twitch, plat_reddit, plat_linkedin, plat_autre) = platform_components
|
| 239 |
|
| 240 |
-
# Purpose
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
purpose_professionnel = gr.Radio(label="Fréquence pour: Professionnel", choices=FREQ_FR)
|
| 247 |
-
purpose_autre_freq = gr.Radio(label="Fréquence pour: Autre (précisez)", choices=FREQ_FR)
|
| 248 |
|
| 249 |
-
|
|
|
|
| 250 |
|
| 251 |
|
| 252 |
-
# === TAB 5 Components
|
| 253 |
demo_gender = gr.Radio(label=LANG["DEMO_GENDER_LABEL"], choices=["Homme", "Femme", "Non-binaire", "Préfère ne pas dire"])
|
| 254 |
demo_age = gr.Number(label=LANG["DEMO_AGE_LABEL"], minimum=18, maximum=100, step=1, placeholder="ex: 25")
|
| 255 |
demo_location_commune = gr.Textbox(label=LANG["DEMO_LOCATION_COMMUNE_LABEL"], placeholder="Nom de la commune")
|
| 256 |
demo_location_arrond = gr.Textbox(label=LANG["DEMO_LOCATION_ARROND_LABEL"], placeholder="ex: 75005")
|
| 257 |
demo_parents_location = gr.Radio(label=LANG["DEMO_PARENTS_LOCATION_LABEL"], choices=["Oui", "Non"])
|
| 258 |
-
|
| 259 |
demo_inscription = gr.CheckboxGroup(label=LANG["DEMO_INSCRIPTION_LABEL"], choices=DEMO_INSCRIPTION_CHOICES)
|
| 260 |
-
demo_inscription_autre_detail = gr.Textbox(label="Précisez 'autre niveau d’étude/diplôme'", visible=False) # PRECISION 11
|
| 261 |
-
|
| 262 |
demo_discipline = gr.CheckboxGroup(label=LANG["DEMO_DISCIPLINE_LABEL"], choices=DEMO_DISCIPLINE_CHOICES)
|
| 263 |
-
demo_discipline_autre_detail = gr.Textbox(label="Précisez 'Autres' disciplines", visible=False) # PRECISION 12 (TOTAL PRECISIONS: 12)
|
| 264 |
-
|
| 265 |
demo_job = gr.Radio(label=LANG["DEMO_JOB_LABEL"], choices=["Oui", "Non"])
|
| 266 |
demo_income = gr.Radio(label=LANG["DEMO_INCOME_LABEL"], choices=INCOME_CHOICES)
|
| 267 |
demo_socialcapital1_parent1 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT1_LABEL"], choices=INSEE_CHOICES)
|
| 268 |
demo_socialcapital1_parent2 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT2_LABEL"], choices=INSEE_CHOICES)
|
| 269 |
demo_socialcapital2 = gr.Number(label=LANG["DEMO_SOCIALCAPITAL2_LABEL"], minimum=0, step=1)
|
| 270 |
|
| 271 |
-
# === TAB 6 Components
|
| 272 |
open_non_institutionnel = gr.Textbox(label=LANG["OPEN_NON_INSTITUTIONNEL_LABEL"], lines=3)
|
| 273 |
open_alternatives = gr.Textbox(label=LANG["OPEN_ALTERNATIVES_LABEL"], placeholder="5 mots maximum")
|
| 274 |
open_motivations = gr.Textbox(label=LANG["OPEN_MOTIVATIONS_LABEL"], lines=3)
|
| 275 |
|
| 276 |
|
| 277 |
-
#
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
# =================================================================
|
| 281 |
-
# DATA COMPONENT FINAL ASSEMBLY
|
| 282 |
-
# --- CRITICAL FIX APPLIED HERE: All 105 components are explicitly listed ---
|
| 283 |
# =================================================================
|
| 284 |
DATA_INPUT_COMPONENTS = [
|
| 285 |
# Global (1)
|
| 286 |
-
enqueteur_id,
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
approach_answer, refusal_reason, refusal_reason_other, contact_later,
|
| 290 |
space1, space2a_1, space2a_2, space2a_3, space2b,
|
| 291 |
-
space3_1, space3_2, space3_3,
|
| 292 |
space4, space5,
|
| 293 |
-
space6,
|
| 294 |
-
space7_1, space_domain_1,
|
| 295 |
space7_2, space_domain_2,
|
| 296 |
space7_3, space_domain_3,
|
| 297 |
-
space11,
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
info3, info3_autre_detail,
|
| 308 |
-
info_opposite_feeling, info_contradict_opinion, info_contradict_opinion_autre_detail,
|
| 309 |
-
info4, info5,
|
| 310 |
-
# INFO6 SLIDERS (9 components: Explicit unpacking via * is safest here)
|
| 311 |
-
*info6_slider_components,
|
| 312 |
-
|
| 313 |
# TAB 4 - Cultural & Digital (30)
|
| 314 |
-
prat_cult1,
|
| 315 |
-
|
| 316 |
-
prat_cult_lecture, prat_cult_cinema, prat_cult_musique, prat_cult_theatre, prat_cult_expositions,
|
| 317 |
prat_cult_sport, prat_cult_voyages, prat_cult_jeux_video, prat_cult_bricolage, prat_cult_reseaux,
|
| 318 |
prat_nature,
|
| 319 |
-
|
| 320 |
-
plat_instagram, plat_tiktok, plat_youtube, plat_twitter, plat_facebook,
|
| 321 |
plat_snapchat, plat_twitch, plat_reddit, plat_linkedin, plat_autre,
|
| 322 |
-
# PURPOSE (8 components)
|
| 323 |
purpose_autre_detail,
|
| 324 |
-
purpose_actu, purpose_loisirs, purpose_education, purpose_engagement,
|
| 325 |
-
purpose_social, purpose_professionnel, purpose_autre_freq,
|
| 326 |
-
|
| 327 |
-
# TAB 5 - Demographics (14)
|
| 328 |
demo_gender, demo_age, demo_location_commune, demo_location_arrond, demo_parents_location,
|
| 329 |
-
demo_inscription,
|
| 330 |
-
demo_discipline, demo_discipline_autre_detail,
|
| 331 |
-
demo_job, demo_income,
|
| 332 |
demo_socialcapital1_parent1, demo_socialcapital1_parent2, demo_socialcapital2,
|
| 333 |
-
|
| 334 |
# TAB 6 - Open Questions (3)
|
| 335 |
open_non_institutionnel, open_alternatives, open_motivations,
|
| 336 |
]
|
| 337 |
|
| 338 |
-
# =================================================================
|
| 339 |
-
# CRITICAL DEBUGGING CHECK
|
| 340 |
-
# =================================================================
|
| 341 |
if len(DATA_INPUT_COMPONENTS) != EXPECTED_DATA_COUNT:
|
| 342 |
-
print(f"DEBUG: Application Startup Check Failed!")
|
| 343 |
-
print(f"DEBUG: Found component count: {len(DATA_INPUT_COMPONENTS)}")
|
| 344 |
-
print(f"DEBUG: Expected count: {EXPECTED_DATA_COUNT}")
|
| 345 |
-
|
| 346 |
-
try:
|
| 347 |
-
last_15_labels = [getattr(c, 'label', 'N/A') for c in DATA_INPUT_COMPONENTS[-15:]]
|
| 348 |
-
print(f"DEBUG: Last 15 component labels found: {last_15_labels}")
|
| 349 |
-
except Exception as e:
|
| 350 |
-
print(f"DEBUG: Could not print last 15 labels: {e}")
|
| 351 |
-
|
| 352 |
-
# This line number should now be around 640 in your full file
|
| 353 |
raise RuntimeError(f"Internal component count mismatch. Expected {EXPECTED_DATA_COUNT}, got {len(DATA_INPUT_COMPONENTS)}. Please verify the DATA_INPUT_COMPONENTS list definition.")
|
| 354 |
|
| 355 |
SUBMIT_INPUT_COMPONENTS = DATA_INPUT_COMPONENTS
|
| 356 |
|
| 357 |
-
|
| 358 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
|
| 360 |
if __name__ == "__main__":
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
|
|
|
|
|
|
|
|
| 6 |
from datetime import datetime
|
| 7 |
|
| 8 |
# =================================================================
|
| 9 |
+
# CONFIGURATION AND LANGUAGE DEFINITIONS
|
| 10 |
# =================================================================
|
| 11 |
|
| 12 |
+
# Setting EXPECTED_DATA_COUNT based on the final verified structure: 91
|
| 13 |
+
EXPECTED_DATA_COUNT = 91
|
| 14 |
+
EXPECTED_COUNT = 91
|
| 15 |
|
| 16 |
# --- Shared Choices (French Only) ---
|
| 17 |
FREQ_FR = ["Jamais", "Rarement", "Parfois", "Souvent"]
|
|
|
|
| 31 |
"Ancrage local (bar associatif, aides aux personnes agées, bibliothèque associative…)",
|
| 32 |
"autre - préciser"
|
| 33 |
]
|
| 34 |
+
# LISTE POUR ENGAGE3
|
| 35 |
ENGAGE3_CHOICES_FR = [
|
| 36 |
+
"écologie",
|
| 37 |
+
"lutte contre la pauvreté",
|
| 38 |
+
"action contre les discriminations",
|
| 39 |
+
"vie politique locale ou nationale",
|
| 40 |
+
"action syndicale",
|
| 41 |
+
"causes internationales",
|
| 42 |
+
"autre / précisez"
|
| 43 |
]
|
| 44 |
SOCIAL_MEDIA_CHOICES = [
|
| 45 |
+
"Instagram", "YouTube", "TikTok", "Facebook", "WhatsApp", "BlueSky", "Signal",
|
| 46 |
+
"Telegram", "Mastodon", "Discord", "Twitter", "Reddit", "messagerie de jeu en ligne",
|
| 47 |
"autre : préciser", "je ne sais pas"
|
| 48 |
]
|
| 49 |
ANXIETY_MANIFESTATION_CHOICES = [
|
|
|
|
| 53 |
"Je fais des recherches sur internet", "J’interroge / je confie à un chatbot",
|
| 54 |
"Je rejoins une communauté spécialisée sur les réseaux sociaux"
|
| 55 |
]
|
| 56 |
+
|
| 57 |
INFO4_CHOICES = ["Curieux", "Indifférent·e", "Légèrement agacé·e", "Inquiet·ète", "En colère", "Déstabilisé·e"]
|
| 58 |
INFO5_CHOICES = [
|
| 59 |
+
"Je le lis/regarde avec attention.",
|
| 60 |
+
"Je le ferme ou l’ignore.",
|
| 61 |
+
"Je cherche des arguments pour le réfuter.",
|
| 62 |
+
"Je le partage pour en discuter avec d’autres.",
|
| 63 |
+
"autre réaction"
|
| 64 |
]
|
| 65 |
+
|
| 66 |
+
ENGAGE1_CHOICES = [
|
| 67 |
+
"non",
|
| 68 |
+
"en tant que sympathisant",
|
| 69 |
+
"comme donateur-rice",
|
| 70 |
+
"en tant que bénévole",
|
| 71 |
+
"comme participant·e/spectateur-rice",
|
| 72 |
+
"comme organisateur-rice/responsable"
|
| 73 |
]
|
| 74 |
+
|
| 75 |
ENGAGE2_CHOICES = [
|
| 76 |
+
"une association",
|
| 77 |
+
"un collectif",
|
| 78 |
+
"une ONG",
|
| 79 |
+
"un parti politique",
|
| 80 |
+
"un syndicat étudiant",
|
| 81 |
+
"une assemblée ou une instance représentative (collectivité, communes…)",
|
| 82 |
+
"autre (préciser)",
|
| 83 |
+
"aucune de ces formes"
|
| 84 |
]
|
| 85 |
+
|
| 86 |
DEMO_INSCRIPTION_CHOICES = ["L1", "L2", "L3", "M1", "M2", "D", "DU", "autre niveau d’étude/diplôme"]
|
| 87 |
DEMO_DISCIPLINE_CHOICES = [
|
| 88 |
+
"Cinéma", "Communication", "Langues", "LEA", "Lettres", "Médiation", "Musique",
|
| 89 |
"Sciences du langage", "Théâtre", "Traduction", "Autres"
|
| 90 |
]
|
| 91 |
+
|
| 92 |
|
| 93 |
# --- Language Definitions (French Only) ---
|
| 94 |
+
LANG_FR = {
|
|
|
|
| 95 |
"TITLE": "Questionnaire : Pratiques culturelles et Engagement Citoyen",
|
| 96 |
"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.",
|
| 97 |
+
"ENQUETEUR_LABEL": "Identifiant de l'enquêteur",
|
| 98 |
"TAB_1_TITLE": "1. Contact & Lieux Fréquentés", "TAB_2_TITLE": "2. Engagement Citoyen",
|
| 99 |
"TAB_3_TITLE": "3. Consommation d’actualités", "TAB_4_TITLE": "4. Pratiques culturelles et usages numériques",
|
| 100 |
"TAB_5_TITLE": "5. Profil Démographique", "TAB_6_TITLE": "6. Questions ouvertes",
|
| 101 |
+
"TAB_7_TITLE": "7. Soumission et Téléchargement",
|
| 102 |
"APPROACH_LABEL": "APPROACH - Accepterais-tu de répondre à un questionnaire ? (10 min)",
|
| 103 |
"REFUSAL_REASON_LABEL": "ANSWER_NO1 (si APPROACH=non) - Pourquoi ?",
|
| 104 |
"CONTACT_LATER_LABEL": "ANSWER_NO2 (si APPROACH=non) - On peut prendre rendez-vous plus tard ?",
|
|
|
|
| 118 |
"SPACE2B_OPTIONS": ["Pas le temps", "Trop cher", "Je ne sais pas où aller", "Pas intéressé", "Autre"],
|
| 119 |
"ENGAGE1_LABEL": "ENGAGE1 - Toi-même, participes-tu à des pratiques d’engagement citoyen ?",
|
| 120 |
"ENGAGEMENT_ORGANISATION_LABEL": "ENGAGE2 - Dans quelle(s) organisation(s) es-tu engagé·e ?",
|
| 121 |
+
"ENGAGE2_AUTRE_DETAIL_LABEL": "ENGAGE2 - Précisez 'autre'",
|
| 122 |
"ENGAGEMENT_DOMAINE_LABEL": "ENGAGE3 - Dans quel(s) domaine(s) es-tu engagé·e ?",
|
| 123 |
+
"ENGAGE3_AUTRE_DETAIL_LABEL": "ENGAGE3 - Précisez 'autre'",
|
| 124 |
"INFO_ACTIVITES_LABEL": "ENGAGE4 - Participes-tu ou as-tu participé à une ou plusieurs des activités suivantes ?",
|
| 125 |
"INFO_RESEAUX_SOCIAUX_LABEL": "ENGAGE5 - Utilises-tu les réseaux sociaux pour t’engager dans des causes ?",
|
| 126 |
"INFO_FREQUENCE_ACTU_LABEL": "INFO1 - À quelle fréquence te tiens-tu informé·e de l’actualité ?",
|
|
|
|
| 129 |
"OPPOSITE_FEELING_BASE_LABEL": "INFO4 - Quand une personne exprime une opinion opposée à la tienne sur le thème qui te semble représenter des enjeux majeurs, comment te sens-tu ?",
|
| 130 |
"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 ?",
|
| 131 |
"INFO4_LABEL": "INFO6 - Parmi ces thèmes, lequel ou lesquels génèrent chez toi un sentiment d’anxiété ?",
|
| 132 |
+
"INFO5_LABEL": "INFO7 - Comment se manifeste ce sentiment d’anxiété ?",
|
| 133 |
"INFO6_TITLE": "INFO8 - Éléments les plus problématiques pour toi (Échelle 1 'sans importance' à 10 'essentiel')",
|
| 134 |
"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"],
|
| 135 |
"PRAT_CULT1_LABEL": "PRAT_CULT1 - Combien de sorties culturelles environ par mois :",
|
|
|
|
| 156 |
"RESET_BUTTON": "Recommencer",
|
| 157 |
}
|
| 158 |
|
| 159 |
+
LANG = LANG_FR
|
| 160 |
|
| 161 |
# =================================================================
|
| 162 |
+
# COMPONENT DEFINITIONS
|
| 163 |
# =================================================================
|
| 164 |
|
| 165 |
# === Global ===
|
| 166 |
enqueteur_id = gr.Textbox(label=LANG["ENQUETEUR_LABEL"], placeholder="Entrez votre identifiant")
|
| 167 |
+
|
| 168 |
+
# === Submission/Output Components ===
|
| 169 |
submit_btn = gr.Button(LANG["SUBMIT_BUTTON"], variant="primary")
|
| 170 |
reset_btn = gr.Button(LANG["RESET_BUTTON"])
|
| 171 |
output_status = gr.Markdown("---")
|
| 172 |
output_message = gr.File(label="Télécharger les Données", file_types=['.csv'], visible=False, elem_id="output_message")
|
| 173 |
|
| 174 |
|
| 175 |
+
# === TAB 1 Components ===
|
| 176 |
approach_answer = gr.Radio(label=LANG["APPROACH_LABEL"], choices=["Oui", "Non"])
|
| 177 |
refusal_reason = gr.CheckboxGroup(label=LANG["REFUSAL_REASON_LABEL"], choices=["Pas le temps", "Cela ne m’intéresse pas", "Autre"], visible=False)
|
| 178 |
+
refusal_reason_other = gr.Textbox(label="Précisez 'Autre'", visible=False)
|
| 179 |
contact_later = gr.Radio(label=LANG["CONTACT_LATER_LABEL"], choices=["Oui", "Non"], visible=False)
|
| 180 |
|
| 181 |
space1 = gr.Radio(label=LANG["SPACE1_LABEL"], choices=FREQ_FR)
|
|
|
|
| 196 |
space7_2 = gr.Textbox(label="Pratique 2", placeholder="Décrivez la pratique d'engagement 2", visible=False)
|
| 197 |
space7_3 = gr.Textbox(label="Pratique 3", placeholder="Décrivez la pratique d'engagement 3", visible=False)
|
| 198 |
|
| 199 |
+
# Domaine components (CheckboxGroup)
|
| 200 |
space_domain_1 = gr.CheckboxGroup(label=f"Domaines pour Pratique 1", choices=DOMAIN_CHOICES_FR, visible=False)
|
| 201 |
space_domain_2 = gr.CheckboxGroup(label=f"Domaines pour Pratique 2", choices=DOMAIN_CHOICES_FR, visible=False)
|
| 202 |
space_domain_3 = gr.CheckboxGroup(label=f"Domaines pour Pratique 3", choices=DOMAIN_CHOICES_FR, visible=False)
|
|
|
|
| 203 |
|
| 204 |
space11 = gr.CheckboxGroup(label=LANG["SPACE11_LABEL"], choices=SOCIAL_MEDIA_CHOICES)
|
|
|
|
|
|
|
| 205 |
|
| 206 |
+
# === TAB 2 Components ===
|
| 207 |
engage1_role = gr.CheckboxGroup(label=LANG["ENGAGE1_LABEL"], choices=ENGAGE1_CHOICES)
|
| 208 |
engage2_type = gr.CheckboxGroup(label=LANG["ENGAGEMENT_ORGANISATION_LABEL"], choices=ENGAGE2_CHOICES)
|
| 209 |
+
engage2_autre_detail = gr.Textbox(label=LANG["ENGAGE2_AUTRE_DETAIL_LABEL"], visible=False)
|
| 210 |
|
| 211 |
+
# ENGAGE3 : CheckboxGroup + Textbox pour 'autre'
|
| 212 |
engagement_domaine = gr.CheckboxGroup(label=LANG["ENGAGEMENT_DOMAINE_LABEL"], choices=ENGAGE3_CHOICES_FR)
|
| 213 |
+
engage3_autre_detail = gr.Textbox(label=LANG["ENGAGE3_AUTRE_DETAIL_LABEL"], visible=False)
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
+
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)
|
| 216 |
info_reseaux_sociaux = gr.Radio(label=LANG["INFO_RESEAUX_SOCIAUX_LABEL"], choices=["Oui", "Non", "Parfois"])
|
| 217 |
|
| 218 |
+
# === TAB 3 Components ===
|
|
|
|
| 219 |
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"])
|
|
|
|
| 220 |
info2 = gr.CheckboxGroup(label=LANG["INFO2_LABEL"], choices=THEMES_ACTU_FR)
|
|
|
|
|
|
|
| 221 |
info3 = gr.CheckboxGroup(label=LANG["INFO3_LABEL"], choices=THEMES_ACTU_FR)
|
|
|
|
|
|
|
| 222 |
info_opposite_feeling = gr.Radio(label=LANG["OPPOSITE_FEELING_BASE_LABEL"], choices=INFO4_CHOICES)
|
|
|
|
| 223 |
info_contradict_opinion = gr.Radio(label=LANG["INFO_CONTRADICT_OPINION_LABEL"], choices=INFO5_CHOICES)
|
|
|
|
|
|
|
| 224 |
info4 = gr.CheckboxGroup(label=LANG["INFO4_LABEL"], choices=THEMES_ACTU_FR)
|
| 225 |
+
info5 = gr.CheckboxGroup(label=LANG["INFO5_LABEL"], choices=ANXIETY_MANIFESTATION_CHOICES)
|
| 226 |
|
| 227 |
# Info 8 Sliders (9 components)
|
| 228 |
info6_sliders = {}
|
|
|
|
| 230 |
clean_id = item.split('/')[0].lower().replace(' ', '_').replace('é', 'e').replace('è', 'e')
|
| 231 |
info6_sliders[clean_id] = gr.Slider(label=item, minimum=1, maximum=10, step=1)
|
| 232 |
info6_slider_components = list(info6_sliders.values())
|
| 233 |
+
info6_logement, info6_politique, info6_etudes, info6_climat, info6_sociales, info6_sentimentale, info6_securite, info6_estime, info6_liberte = info6_slider_components
|
| 234 |
|
| 235 |
|
| 236 |
+
# === TAB 4 Components ===
|
| 237 |
prat_cult1 = gr.Number(label=LANG["PRAT_CULT1_LABEL"], minimum=0, maximum=100, step=1)
|
| 238 |
|
| 239 |
# Cultural Frequency Radios (10 components)
|
| 240 |
prat_cult_freq_components = [
|
| 241 |
gr.Radio(label=f"{activity}", choices=FREQ_FR) for activity in ACTIVITY_CHOICES
|
| 242 |
]
|
| 243 |
+
(prat_cult_lecture, prat_cult_cinema, prat_cult_musique, prat_cult_theatre, prat_cult_expositions,
|
| 244 |
prat_cult_sport, prat_cult_voyages, prat_cult_jeux_video, prat_cult_bricolage, prat_cult_reseaux) = prat_cult_freq_components
|
| 245 |
|
| 246 |
prat_nature = gr.Radio(label=LANG["PRAT_NATURE_LABEL"], choices=["Oui", "Non", "Parfois"])
|
|
|
|
| 249 |
platform_components = [
|
| 250 |
gr.Number(label=f"Heures/jour pour {platform}", placeholder="0.5, 1, 2...", minimum=0, step=0.5) for platform in PLATFORM_CHOICES
|
| 251 |
]
|
| 252 |
+
(plat_instagram, plat_tiktok, plat_youtube, plat_twitter, plat_facebook,
|
| 253 |
plat_snapchat, plat_twitch, plat_reddit, plat_linkedin, plat_autre) = platform_components
|
| 254 |
|
| 255 |
+
# Purpose Frequency Radios (7 components)
|
| 256 |
+
purpose_components = [
|
| 257 |
+
gr.Radio(label=f"Fréquence pour: {purpose}", choices=FREQ_FR) for purpose in PURPOSE_CHOICES[:-1]
|
| 258 |
+
]
|
| 259 |
+
purpose_autre_detail = gr.Textbox(label="Précisez l'usage 'Autre'", visible=True)
|
| 260 |
+
purpose_components.append(gr.Radio(label=f"Fréquence pour: {PURPOSE_CHOICES[-1]}", choices=FREQ_FR))
|
|
|
|
|
|
|
| 261 |
|
| 262 |
+
(purpose_actu, purpose_loisirs, purpose_education, purpose_engagement,
|
| 263 |
+
purpose_social, purpose_professionnel, purpose_autre_freq) = purpose_components
|
| 264 |
|
| 265 |
|
| 266 |
+
# === TAB 5 Components ===
|
| 267 |
demo_gender = gr.Radio(label=LANG["DEMO_GENDER_LABEL"], choices=["Homme", "Femme", "Non-binaire", "Préfère ne pas dire"])
|
| 268 |
demo_age = gr.Number(label=LANG["DEMO_AGE_LABEL"], minimum=18, maximum=100, step=1, placeholder="ex: 25")
|
| 269 |
demo_location_commune = gr.Textbox(label=LANG["DEMO_LOCATION_COMMUNE_LABEL"], placeholder="Nom de la commune")
|
| 270 |
demo_location_arrond = gr.Textbox(label=LANG["DEMO_LOCATION_ARROND_LABEL"], placeholder="ex: 75005")
|
| 271 |
demo_parents_location = gr.Radio(label=LANG["DEMO_PARENTS_LOCATION_LABEL"], choices=["Oui", "Non"])
|
|
|
|
| 272 |
demo_inscription = gr.CheckboxGroup(label=LANG["DEMO_INSCRIPTION_LABEL"], choices=DEMO_INSCRIPTION_CHOICES)
|
|
|
|
|
|
|
| 273 |
demo_discipline = gr.CheckboxGroup(label=LANG["DEMO_DISCIPLINE_LABEL"], choices=DEMO_DISCIPLINE_CHOICES)
|
|
|
|
|
|
|
| 274 |
demo_job = gr.Radio(label=LANG["DEMO_JOB_LABEL"], choices=["Oui", "Non"])
|
| 275 |
demo_income = gr.Radio(label=LANG["DEMO_INCOME_LABEL"], choices=INCOME_CHOICES)
|
| 276 |
demo_socialcapital1_parent1 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT1_LABEL"], choices=INSEE_CHOICES)
|
| 277 |
demo_socialcapital1_parent2 = gr.Radio(label=LANG["DEMO_SOCIALCAPITAL1_PARENT2_LABEL"], choices=INSEE_CHOICES)
|
| 278 |
demo_socialcapital2 = gr.Number(label=LANG["DEMO_SOCIALCAPITAL2_LABEL"], minimum=0, step=1)
|
| 279 |
|
| 280 |
+
# === TAB 6 Components ===
|
| 281 |
open_non_institutionnel = gr.Textbox(label=LANG["OPEN_NON_INSTITUTIONNEL_LABEL"], lines=3)
|
| 282 |
open_alternatives = gr.Textbox(label=LANG["OPEN_ALTERNATIVES_LABEL"], placeholder="5 mots maximum")
|
| 283 |
open_motivations = gr.Textbox(label=LANG["OPEN_MOTIVATIONS_LABEL"], lines=3)
|
| 284 |
|
| 285 |
|
| 286 |
+
# Helper function to generate robust column names
|
| 287 |
+
def get_column_names(data_components: list) -> list:
|
| 288 |
+
names = []
|
| 289 |
+
for i, component in enumerate(data_components):
|
| 290 |
+
label_attr = getattr(component, 'label', None)
|
| 291 |
+
|
| 292 |
+
if label_attr:
|
| 293 |
+
name = label_attr
|
| 294 |
+
# Nettoyage et simplification du nom de colonne
|
| 295 |
+
clean_name = name.split('-')[0].strip().replace(' ', '_').replace('.', '').replace(':', '')
|
| 296 |
+
|
| 297 |
+
# Gestion des noms spécifiques
|
| 298 |
+
if "Domaines pour Pratique 1" in name:
|
| 299 |
+
clean_name = "P1_DOMAINES"
|
| 300 |
+
elif "Domaines pour Pratique 2" in name:
|
| 301 |
+
clean_name = "P2_DOMAINES"
|
| 302 |
+
elif "Domaines pour Pratique 3" in name:
|
| 303 |
+
clean_name = "P3_DOMAINES"
|
| 304 |
+
elif "Alternatif/Underground" in name:
|
| 305 |
+
clean_name = "SPACE4_QUAL_1"
|
| 306 |
+
elif "Lieu 2 : Alternatif" in name:
|
| 307 |
+
clean_name = "SPACE4_QUAL_2"
|
| 308 |
+
elif "Lieu 3 : Alternatif" in name:
|
| 309 |
+
clean_name = "SPACE4_QUAL_3"
|
| 310 |
+
elif clean_name == "ENGAGE2_Précisez_'autre'":
|
| 311 |
+
clean_name = "ENGAGE2_Autre_Precision"
|
| 312 |
+
# NOM DE COLONNE POUR ENGAGE3_AUTRE
|
| 313 |
+
elif clean_name == "ENGAGE3_Précisez_'autre'":
|
| 314 |
+
clean_name = "ENGAGE3_Autre_Precision"
|
| 315 |
+
elif clean_name == "ENGAGE3_Dans_quel(s)_domaine(s)_es-tu_engagé·e_":
|
| 316 |
+
clean_name = "ENGAGE3_DOMAINE"
|
| 317 |
+
|
| 318 |
+
names.append(clean_name)
|
| 319 |
+
else:
|
| 320 |
+
names.append(f"Component_{i+1}")
|
| 321 |
+
|
| 322 |
+
return names
|
| 323 |
+
|
| 324 |
+
# =================================================================
|
| 325 |
+
# GRADIO UI & LOGIC - UTILITY FUNCTIONS
|
| 326 |
+
# =================================================================
|
| 327 |
+
|
| 328 |
+
def update_visibility_approach(approach):
|
| 329 |
+
updates = {}
|
| 330 |
+
is_non = approach == "Non"
|
| 331 |
+
updates[refusal_reason] = gr.update(visible=is_non)
|
| 332 |
+
updates[contact_later] = gr.update(visible=is_non)
|
| 333 |
+
|
| 334 |
+
if not is_non:
|
| 335 |
+
updates[refusal_reason_other] = gr.update(visible=False)
|
| 336 |
+
|
| 337 |
+
return updates[refusal_reason], updates[contact_later]
|
| 338 |
+
|
| 339 |
+
def update_visibility_refusal(reasons):
|
| 340 |
+
if reasons and "Autre" in reasons:
|
| 341 |
+
return gr.update(visible=True)
|
| 342 |
+
return gr.update(visible=False)
|
| 343 |
+
|
| 344 |
+
def update_visibility_space1(frequency):
|
| 345 |
+
is_frequent = frequency in ["Rarement", "Parfois", "Souvent"]
|
| 346 |
+
is_never = frequency == "Jamais"
|
| 347 |
+
return {
|
| 348 |
+
space2a_1: gr.update(visible=is_frequent),
|
| 349 |
+
space2a_2: gr.update(visible=is_frequent),
|
| 350 |
+
space2a_3: gr.update(visible=is_frequent),
|
| 351 |
+
space3_1: gr.update(visible=is_frequent),
|
| 352 |
+
space3_2: gr.update(visible=is_frequent),
|
| 353 |
+
space3_3: gr.update(visible=is_frequent),
|
| 354 |
+
space2b: gr.update(visible=is_never),
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
def update_visibility_space4(follow_community):
|
| 358 |
+
return gr.update(visible=follow_community == "Oui")
|
| 359 |
+
|
| 360 |
+
def update_visibility_space6(knows_practices):
|
| 361 |
+
is_yes = knows_practices == "Oui"
|
| 362 |
+
outputs = {
|
| 363 |
+
space7_1: gr.update(visible=is_yes),
|
| 364 |
+
space7_2: gr.update(visible=is_yes),
|
| 365 |
+
space7_3: gr.update(visible=is_yes),
|
| 366 |
+
}
|
| 367 |
+
# If not "Oui", hide both the practice description and its domains
|
| 368 |
+
if not is_yes:
|
| 369 |
+
outputs[space_domain_1] = gr.update(visible=False)
|
| 370 |
+
outputs[space_domain_2] = gr.update(visible=False)
|
| 371 |
+
outputs[space_domain_3] = gr.update(visible=False)
|
| 372 |
+
# If "Oui", the practice description (space7_X) is visible, but the domains (space_domain_X)
|
| 373 |
+
# must be hidden by default until a practice is actually typed in (handled by space7_X.change)
|
| 374 |
+
else:
|
| 375 |
+
outputs[space_domain_1] = gr.update(visible=False)
|
| 376 |
+
outputs[space_domain_2] = gr.update(visible=False)
|
| 377 |
+
outputs[space_domain_3] = gr.update(visible=False)
|
| 378 |
+
|
| 379 |
+
return outputs
|
| 380 |
+
|
| 381 |
+
def update_domain_visibility(practice_text):
|
| 382 |
+
is_cited = bool(practice_text.strip())
|
| 383 |
+
# Note: CheckboxGroup (or Radio) is now visible if the corresponding practice text is present
|
| 384 |
+
return gr.update(visible=is_cited)
|
| 385 |
+
|
| 386 |
+
def update_visibility_engage2_autre(engage2_choices: List[str]):
|
| 387 |
+
return gr.update(visible="autre (préciser)" in engage2_choices)
|
| 388 |
+
|
| 389 |
+
# NOUVELLE FONCTION de visibilité pour ENGAGE3
|
| 390 |
+
def update_visibility_engage3_autre(engage3_choices: List[str]):
|
| 391 |
+
return gr.update(visible="autre / précisez" in engage3_choices)
|
| 392 |
+
|
| 393 |
+
def process_survey(*data_values: Any) -> Tuple[str, gr.File]:
|
| 394 |
+
"""
|
| 395 |
+
Processes survey data, creates a temporary CSV file, and returns the path
|
| 396 |
+
and a success message for download.
|
| 397 |
+
"""
|
| 398 |
+
|
| 399 |
+
all_inputs_list = list(data_values)
|
| 400 |
+
|
| 401 |
+
if len(all_inputs_list) != EXPECTED_COUNT:
|
| 402 |
+
error_msg = f"Erreur critique: Le nombre d'entrées reçues est incorrect ({len(all_inputs_list)} au lieu de {EXPECTED_COUNT}). Veuillez contacter le développeur."
|
| 403 |
+
return error_msg, gr.update(visible=False, value=None)
|
| 404 |
+
|
| 405 |
+
language = "FR"
|
| 406 |
+
|
| 407 |
+
try:
|
| 408 |
+
column_names = get_column_names(DATA_INPUT_COMPONENTS)
|
| 409 |
+
column_names.insert(0, "DEMO_LANGUAGE")
|
| 410 |
+
|
| 411 |
+
data_row = [language] + all_inputs_list
|
| 412 |
+
|
| 413 |
+
df = pd.DataFrame([data_row], columns=column_names)
|
| 414 |
+
|
| 415 |
+
# Nommage du fichier: Enquêteur ID + Date
|
| 416 |
+
enqueteur_id_val = all_inputs_list[0]
|
| 417 |
+
current_date_str = datetime.now().strftime("%Y-%m-%d")
|
| 418 |
+
|
| 419 |
+
safe_enqueteur_id = str(enqueteur_id_val).strip()
|
| 420 |
+
safe_enqueteur_id = "".join(c if c.isalnum() else '_' for c in safe_enqueteur_id)
|
| 421 |
+
if not safe_enqueteur_id:
|
| 422 |
+
safe_enqueteur_id = "NO_ID"
|
| 423 |
+
|
| 424 |
+
temp_filename = f"/tmp/{safe_enqueteur_id}_{current_date_str}.csv"
|
| 425 |
+
os.makedirs(os.path.dirname(temp_filename), exist_ok=True)
|
| 426 |
+
df.to_csv(temp_filename, index=False, encoding='utf-8')
|
| 427 |
+
|
| 428 |
+
success_msg = f"✨ Succès ! Le questionnaire a été soumis. Cliquez sur le lien ci-dessous pour **Télécharger le Fichier CSV** nommé `{safe_enqueteur_id}_{current_date_str}.csv`."
|
| 429 |
+
|
| 430 |
+
return success_msg, gr.update(value=temp_filename, visible=True)
|
| 431 |
+
|
| 432 |
+
except Exception as e:
|
| 433 |
+
error_msg = f"Erreur lors de la génération du CSV: {str(e)}"
|
| 434 |
+
return error_msg, gr.update(visible=False, value=None)
|
| 435 |
+
|
| 436 |
|
| 437 |
# =================================================================
|
| 438 |
+
# DATA COMPONENT FINAL ASSEMBLY
|
|
|
|
| 439 |
# =================================================================
|
| 440 |
DATA_INPUT_COMPONENTS = [
|
| 441 |
# Global (1)
|
| 442 |
+
enqueteur_id,
|
| 443 |
+
# TAB 1 - Contact & Lieux (22)
|
| 444 |
+
approach_answer, refusal_reason, refusal_reason_other, contact_later,
|
|
|
|
| 445 |
space1, space2a_1, space2a_2, space2a_3, space2b,
|
| 446 |
+
space3_1, space3_2, space3_3,
|
| 447 |
space4, space5,
|
| 448 |
+
space6,
|
| 449 |
+
space7_1, space_domain_1,
|
| 450 |
space7_2, space_domain_2,
|
| 451 |
space7_3, space_domain_3,
|
| 452 |
+
space11,
|
| 453 |
+
# TAB 2 - Engagement (7)
|
| 454 |
+
engage1_role, engage2_type, engage2_autre_detail,
|
| 455 |
+
engagement_domaine, engage3_autre_detail,
|
| 456 |
+
info_activites, info_reseaux_sociaux,
|
| 457 |
+
# TAB 3 - Actualité (16)
|
| 458 |
+
info_frequence_actu, info2, info3, info_opposite_feeling, info_contradict_opinion,
|
| 459 |
+
info4, info5,
|
| 460 |
+
info6_logement, info6_politique, info6_etudes, info6_climat, info6_sociales,
|
| 461 |
+
info6_sentimentale, info6_securite, info6_estime, info6_liberte,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
# TAB 4 - Cultural & Digital (30)
|
| 463 |
+
prat_cult1,
|
| 464 |
+
prat_cult_lecture, prat_cult_cinema, prat_cult_musique, prat_cult_theatre, prat_cult_expositions,
|
|
|
|
| 465 |
prat_cult_sport, prat_cult_voyages, prat_cult_jeux_video, prat_cult_bricolage, prat_cult_reseaux,
|
| 466 |
prat_nature,
|
| 467 |
+
plat_instagram, plat_tiktok, plat_youtube, plat_twitter, plat_facebook,
|
|
|
|
| 468 |
plat_snapchat, plat_twitch, plat_reddit, plat_linkedin, plat_autre,
|
|
|
|
| 469 |
purpose_autre_detail,
|
| 470 |
+
purpose_actu, purpose_loisirs, purpose_education, purpose_engagement,
|
| 471 |
+
purpose_social, purpose_professionnel, purpose_autre_freq,
|
| 472 |
+
# TAB 5 - Demographics (12)
|
|
|
|
| 473 |
demo_gender, demo_age, demo_location_commune, demo_location_arrond, demo_parents_location,
|
| 474 |
+
demo_inscription, demo_discipline, demo_job, demo_income,
|
|
|
|
|
|
|
| 475 |
demo_socialcapital1_parent1, demo_socialcapital1_parent2, demo_socialcapital2,
|
|
|
|
| 476 |
# TAB 6 - Open Questions (3)
|
| 477 |
open_non_institutionnel, open_alternatives, open_motivations,
|
| 478 |
]
|
| 479 |
|
|
|
|
|
|
|
|
|
|
| 480 |
if len(DATA_INPUT_COMPONENTS) != EXPECTED_DATA_COUNT:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
raise RuntimeError(f"Internal component count mismatch. Expected {EXPECTED_DATA_COUNT}, got {len(DATA_INPUT_COMPONENTS)}. Please verify the DATA_INPUT_COMPONENTS list definition.")
|
| 482 |
|
| 483 |
SUBMIT_INPUT_COMPONENTS = DATA_INPUT_COMPONENTS
|
| 484 |
|
| 485 |
+
|
| 486 |
+
# =================================================================
|
| 487 |
+
# GRADIO UI SETUP (Utilisation des Accordions pour le F2F)
|
| 488 |
+
# =================================================================
|
| 489 |
+
|
| 490 |
+
with gr.Blocks(title=LANG["TITLE"], css=".gradio-container { max-width: 1200px; }") as demo:
|
| 491 |
+
|
| 492 |
+
gr.Markdown(f"## {LANG['TITLE']}")
|
| 493 |
+
gr.Markdown(LANG["INTRO_TEXT"])
|
| 494 |
+
|
| 495 |
+
with gr.Row():
|
| 496 |
+
enqueteur_id.render()
|
| 497 |
+
|
| 498 |
+
# --- Bloc Accordions ---
|
| 499 |
+
|
| 500 |
+
# =================================================================
|
| 501 |
+
# ACCORDION 1: Contact & Lieux Fréquentés (Ouvert par défaut)
|
| 502 |
+
# =================================================================
|
| 503 |
+
with gr.Accordion(LANG["TAB_1_TITLE"], open=True):
|
| 504 |
+
gr.Markdown("### Phase d'approche")
|
| 505 |
+
with gr.Row():
|
| 506 |
+
approach_answer.render()
|
| 507 |
+
with gr.Column() as refusal_block:
|
| 508 |
+
refusal_reason.render()
|
| 509 |
+
refusal_reason_other.render()
|
| 510 |
+
contact_later.render()
|
| 511 |
+
|
| 512 |
+
approach_answer.change(
|
| 513 |
+
update_visibility_approach,
|
| 514 |
+
inputs=[approach_answer],
|
| 515 |
+
outputs=[refusal_reason, contact_later]
|
| 516 |
+
)
|
| 517 |
+
refusal_reason.change(update_visibility_refusal, inputs=[refusal_reason], outputs=[refusal_reason_other])
|
| 518 |
+
|
| 519 |
+
gr.Markdown("### Lieux et Communautés")
|
| 520 |
+
space1.render()
|
| 521 |
+
gr.Markdown(f"#### {LANG['SPACE2A_TITLE']}")
|
| 522 |
+
with gr.Row() as space2_row:
|
| 523 |
+
space2a_1.render()
|
| 524 |
+
space2a_2.render()
|
| 525 |
+
space2a_3.render()
|
| 526 |
+
|
| 527 |
+
gr.Markdown(f"#### {LANG['SPACE3_TITLE']}")
|
| 528 |
+
with gr.Row() as space3_row:
|
| 529 |
+
space3_1.render()
|
| 530 |
+
space3_2.render()
|
| 531 |
+
space3_3.render()
|
| 532 |
+
|
| 533 |
+
space2b.render()
|
| 534 |
+
space1.change(update_visibility_space1, inputs=[space1], outputs=[space2a_1, space2a_2, space2a_3, space3_1, space3_2, space3_3, space2b])
|
| 535 |
+
|
| 536 |
+
gr.Markdown("### Usages alternatifs et Engagement")
|
| 537 |
+
space4.render()
|
| 538 |
+
space5.render()
|
| 539 |
+
space4.change(update_visibility_space4, inputs=[space4], outputs=[space5])
|
| 540 |
+
|
| 541 |
+
space6.render()
|
| 542 |
+
gr.Markdown(f"#### {LANG['SPACE7_TITLE']} (3 max.) et leurs Domaines (Choix multiples)")
|
| 543 |
+
|
| 544 |
+
# Pratique 1 et son Domaine (Mis côte-à-côte)
|
| 545 |
+
with gr.Row():
|
| 546 |
+
with gr.Column(scale=1): # Moins de place pour le champ de texte
|
| 547 |
+
space7_1.render()
|
| 548 |
+
with gr.Column(scale=2): # Plus de place pour les cases à cocher
|
| 549 |
+
space_domain_1.render()
|
| 550 |
+
|
| 551 |
+
# Pratique 2 et son Domaine (Mis côte-à-côte)
|
| 552 |
+
with gr.Row():
|
| 553 |
+
with gr.Column(scale=1):
|
| 554 |
+
space7_2.render()
|
| 555 |
+
with gr.Column(scale=2):
|
| 556 |
+
space_domain_2.render()
|
| 557 |
+
|
| 558 |
+
# Pratique 3 et son Domaine (Mis côte-à-côte)
|
| 559 |
+
with gr.Row():
|
| 560 |
+
with gr.Column(scale=1):
|
| 561 |
+
space7_3.render()
|
| 562 |
+
with gr.Column(scale=2):
|
| 563 |
+
space_domain_3.render()
|
| 564 |
+
|
| 565 |
+
gr.Markdown(f"#### {LANG['SPACE11_LABEL']}")
|
| 566 |
+
space11.render()
|
| 567 |
+
|
| 568 |
+
# Logique de visibilité pour SPACE7 et les domaines
|
| 569 |
+
space6.change(
|
| 570 |
+
fn=update_visibility_space6,
|
| 571 |
+
inputs=[space6],
|
| 572 |
+
outputs=[space7_1, space7_2, space7_3, space_domain_1, space_domain_2, space_domain_3]
|
| 573 |
+
)
|
| 574 |
+
|
| 575 |
+
# Les domaines ne sont visibles que si une pratique est citée
|
| 576 |
+
space7_1.change(fn=update_domain_visibility, inputs=[space7_1], outputs=[space_domain_1])
|
| 577 |
+
space7_2.change(fn=update_domain_visibility, inputs=[space7_2], outputs=[space_domain_2])
|
| 578 |
+
space7_3.change(fn=update_domain_visibility, inputs=[space7_3], outputs=[space_domain_3])
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
# =================================================================
|
| 582 |
+
# ACCORDION 2: Engagement Citoyen
|
| 583 |
+
# =================================================================
|
| 584 |
+
with gr.Accordion(LANG["TAB_2_TITLE"], open=False):
|
| 585 |
+
engage1_role.render()
|
| 586 |
+
engage2_type.render()
|
| 587 |
+
engage2_autre_detail.render()
|
| 588 |
+
|
| 589 |
+
# ENGAGE3 - Domaine de l'engagement (CheckboxGroup)
|
| 590 |
+
# Utilisation de gr.Box pour mettre en évidence ce bloc de questions.
|
| 591 |
+
with gr.Box(label="Domaine(s) de l'engagement"):
|
| 592 |
+
engagement_domaine.render()
|
| 593 |
+
engage3_autre_detail.render() # Champ de précision pour 'autre'
|
| 594 |
+
|
| 595 |
+
info_activites.render()
|
| 596 |
+
info_reseaux_sociaux.render()
|
| 597 |
+
|
| 598 |
+
engage2_type.change(
|
| 599 |
+
fn=update_visibility_engage2_autre,
|
| 600 |
+
inputs=[engage2_type],
|
| 601 |
+
outputs=[engage2_autre_detail]
|
| 602 |
+
)
|
| 603 |
+
|
| 604 |
+
# LOGIQUE pour ENGAGE3 'autre'
|
| 605 |
+
engagement_domaine.change(
|
| 606 |
+
fn=update_visibility_engage3_autre,
|
| 607 |
+
inputs=[engagement_domaine],
|
| 608 |
+
outputs=[engage3_autre_detail]
|
| 609 |
+
)
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
# =================================================================
|
| 613 |
+
# ACCORDION 3: Consommation d’actualités
|
| 614 |
+
# =================================================================
|
| 615 |
+
with gr.Accordion(LANG["TAB_3_TITLE"], open=False):
|
| 616 |
+
info_frequence_actu.render()
|
| 617 |
+
info2.render()
|
| 618 |
+
info3.render()
|
| 619 |
+
info_opposite_feeling.render()
|
| 620 |
+
info_contradict_opinion.render()
|
| 621 |
+
gr.Markdown("---")
|
| 622 |
+
info4.render()
|
| 623 |
+
info5.render()
|
| 624 |
+
|
| 625 |
+
gr.Markdown(f"### {LANG['INFO6_TITLE']}")
|
| 626 |
+
with gr.Column():
|
| 627 |
+
info6_logement.render()
|
| 628 |
+
info6_politique.render()
|
| 629 |
+
info6_etudes.render()
|
| 630 |
+
info6_climat.render()
|
| 631 |
+
info6_sociales.render()
|
| 632 |
+
info6_sentimentale.render()
|
| 633 |
+
info6_securite.render()
|
| 634 |
+
info6_estime.render()
|
| 635 |
+
info6_liberte.render()
|
| 636 |
+
|
| 637 |
+
# =================================================================
|
| 638 |
+
# ACCORDION 4: Pratiques culturelles et usages numériques
|
| 639 |
+
# =================================================================
|
| 640 |
+
with gr.Accordion(LANG["TAB_4_TITLE"], open=False):
|
| 641 |
+
prat_cult1.render()
|
| 642 |
+
gr.Markdown(f"### {LANG['PRAT_CULT_PRACTICES_TITLE']}")
|
| 643 |
+
with gr.Row():
|
| 644 |
+
with gr.Column():
|
| 645 |
+
prat_cult_lecture.render()
|
| 646 |
+
prat_cult_cinema.render()
|
| 647 |
+
prat_cult_musique.render()
|
| 648 |
+
prat_cult_theatre.render()
|
| 649 |
+
prat_cult_expositions.render()
|
| 650 |
+
with gr.Column():
|
| 651 |
+
prat_cult_sport.render()
|
| 652 |
+
prat_cult_voyages.render()
|
| 653 |
+
prat_cult_jeux_video.render()
|
| 654 |
+
prat_cult_bricolage.render()
|
| 655 |
+
prat_cult_reseaux.render()
|
| 656 |
+
prat_nature.render()
|
| 657 |
+
gr.Markdown(f"### {LANG['PLATFORM_TITLE']}")
|
| 658 |
+
with gr.Row():
|
| 659 |
+
with gr.Column():
|
| 660 |
+
plat_instagram.render()
|
| 661 |
+
plat_tiktok.render()
|
| 662 |
+
plat_youtube.render()
|
| 663 |
+
plat_twitter.render()
|
| 664 |
+
plat_facebook.render()
|
| 665 |
+
with gr.Column():
|
| 666 |
+
plat_snapchat.render()
|
| 667 |
+
plat_twitch.render()
|
| 668 |
+
plat_reddit.render()
|
| 669 |
+
plat_linkedin.render()
|
| 670 |
+
plat_autre.render()
|
| 671 |
+
gr.Markdown(f"### {LANG['PURPOSE_TITLE']}")
|
| 672 |
+
with gr.Row():
|
| 673 |
+
with gr.Column():
|
| 674 |
+
purpose_actu.render()
|
| 675 |
+
purpose_loisirs.render()
|
| 676 |
+
purpose_education.render()
|
| 677 |
+
purpose_engagement.render()
|
| 678 |
+
with gr.Column():
|
| 679 |
+
purpose_social.render()
|
| 680 |
+
purpose_professionnel.render()
|
| 681 |
+
purpose_autre_freq.render()
|
| 682 |
+
purpose_autre_detail.render()
|
| 683 |
+
|
| 684 |
+
|
| 685 |
+
# =================================================================
|
| 686 |
+
# ACCORDION 5: Profil Démographique
|
| 687 |
+
# =================================================================
|
| 688 |
+
with gr.Accordion(LANG["TAB_5_TITLE"], open=False):
|
| 689 |
+
demo_gender.render()
|
| 690 |
+
demo_age.render()
|
| 691 |
+
with gr.Row():
|
| 692 |
+
demo_location_commune.render()
|
| 693 |
+
demo_location_arrond.render()
|
| 694 |
+
demo_parents_location.render()
|
| 695 |
+
with gr.Row():
|
| 696 |
+
demo_inscription.render()
|
| 697 |
+
demo_discipline.render()
|
| 698 |
+
with gr.Row():
|
| 699 |
+
demo_job.render()
|
| 700 |
+
demo_income.render()
|
| 701 |
+
gr.Markdown("### Capital Social")
|
| 702 |
+
with gr.Row():
|
| 703 |
+
demo_socialcapital1_parent1.render()
|
| 704 |
+
demo_socialcapital1_parent2.render()
|
| 705 |
+
demo_socialcapital2.render()
|
| 706 |
+
|
| 707 |
+
# =================================================================
|
| 708 |
+
# ACCORDION 6: Questions ouvertes
|
| 709 |
+
# =================================================================
|
| 710 |
+
with gr.Accordion(LANG["TAB_6_TITLE"], open=False):
|
| 711 |
+
open_non_institutionnel.render()
|
| 712 |
+
open_alternatives.render()
|
| 713 |
+
open_motivations.render()
|
| 714 |
+
|
| 715 |
+
# =================================================================
|
| 716 |
+
# Soumission et Téléchargement
|
| 717 |
+
# =================================================================
|
| 718 |
+
with gr.Accordion(LANG["TAB_7_TITLE"], open=False):
|
| 719 |
+
gr.Markdown("### Finalisation du Questionnaire")
|
| 720 |
+
gr.Markdown("Vérifiez que toutes vos réponses sont complètes avant de soumettre. Une fois soumis, le fichier CSV (nommé avec l'identifiant de l'enquêteur et la date) apparaîtra ci-dessous pour téléchargement.")
|
| 721 |
+
|
| 722 |
+
with gr.Row():
|
| 723 |
+
submit_btn.render()
|
| 724 |
+
reset_btn.render()
|
| 725 |
+
|
| 726 |
+
output_status.render()
|
| 727 |
+
output_message.render()
|
| 728 |
+
|
| 729 |
+
|
| 730 |
+
# Submit action
|
| 731 |
+
submit_btn.click(
|
| 732 |
+
fn=process_survey,
|
| 733 |
+
inputs=SUBMIT_INPUT_COMPONENTS,
|
| 734 |
+
outputs=[output_status, output_message]
|
| 735 |
+
)
|
| 736 |
+
|
| 737 |
+
# Reset action
|
| 738 |
+
reset_btn.click(
|
| 739 |
+
fn=lambda: ([None] * EXPECTED_DATA_COUNT) + ["---", gr.update(value=None, visible=False)],
|
| 740 |
+
inputs=[],
|
| 741 |
+
outputs=DATA_INPUT_COMPONENTS + [output_status, output_message],
|
| 742 |
+
js="() => { document.getElementById('output_message').innerHTML = '---'; }"
|
| 743 |
+
)
|
| 744 |
|
| 745 |
if __name__ == "__main__":
|
| 746 |
+
import sys
|
| 747 |
+
sys.modules[__name__].EXPECTED_DATA_COUNT = len(DATA_INPUT_COMPONENTS)
|
| 748 |
+
sys.modules[__name__].EXPECTED_COUNT = len(DATA_INPUT_COMPONENTS)
|
| 749 |
+
|
| 750 |
+
demo.launch()
|