mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 08:22:10 +01:00 
			
		
		
		
	Add survey feature
This commit is contained in:
		@@ -7,7 +7,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: TFJM\n"
 | 
					"Project-Id-Version: TFJM\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2025-03-09 11:04+0100\n"
 | 
					"POT-Creation-Date: 2025-03-19 23:07+0100\n"
 | 
				
			||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
					"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
				
			||||||
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
 | 
					"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
 | 
				
			||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
					"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
				
			||||||
@@ -114,6 +114,7 @@ msgstr ""
 | 
				
			|||||||
#: registration/models.py:158 registration/models.py:754
 | 
					#: registration/models.py:158 registration/models.py:754
 | 
				
			||||||
#: registration/tables.py:39
 | 
					#: registration/tables.py:39
 | 
				
			||||||
#: registration/templates/registration/payment_form.html:52
 | 
					#: registration/templates/registration/payment_form.html:52
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:60
 | 
				
			||||||
msgid "team"
 | 
					msgid "team"
 | 
				
			||||||
msgstr "équipe"
 | 
					msgstr "équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -217,7 +218,7 @@ msgstr ""
 | 
				
			|||||||
msgid "Toggle fullscreen mode"
 | 
					msgid "Toggle fullscreen mode"
 | 
				
			||||||
msgstr "Inverse le mode plein écran"
 | 
					msgstr "Inverse le mode plein écran"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/templates/chat/content.html:76 tfjm/templates/navbar.html:126
 | 
					#: chat/templates/chat/content.html:76 tfjm/templates/navbar.html:129
 | 
				
			||||||
msgid "Log out"
 | 
					msgid "Log out"
 | 
				
			||||||
msgstr "Déconnexion"
 | 
					msgstr "Déconnexion"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -251,7 +252,7 @@ msgstr "Chat"
 | 
				
			|||||||
#: chat/templates/chat/login.html:10 chat/templates/chat/login.html:36
 | 
					#: chat/templates/chat/login.html:10 chat/templates/chat/login.html:36
 | 
				
			||||||
#: registration/templates/registration/password_reset_complete.html:10
 | 
					#: registration/templates/registration/password_reset_complete.html:10
 | 
				
			||||||
#: tfjm/templates/base.html:89 tfjm/templates/base.html:90
 | 
					#: tfjm/templates/base.html:89 tfjm/templates/base.html:90
 | 
				
			||||||
#: tfjm/templates/navbar.html:107
 | 
					#: tfjm/templates/navbar.html:110
 | 
				
			||||||
#: tfjm/templates/registration/includes/login.html:22
 | 
					#: tfjm/templates/registration/includes/login.html:22
 | 
				
			||||||
#: tfjm/templates/registration/login.html:7
 | 
					#: tfjm/templates/registration/login.html:7
 | 
				
			||||||
#: tfjm/templates/registration/login.html:8
 | 
					#: tfjm/templates/registration/login.html:8
 | 
				
			||||||
@@ -713,7 +714,7 @@ msgstr ""
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: draw/models.py:496 draw/models.py:519 participation/models.py:1258
 | 
					#: draw/models.py:496 draw/models.py:519 participation/models.py:1258
 | 
				
			||||||
#: participation/models.py:1695 participation/models.py:1933
 | 
					#: participation/models.py:1695 participation/models.py:1933
 | 
				
			||||||
#: participation/views.py:1495 participation/views.py:1760
 | 
					#: participation/views.py:1501 participation/views.py:1766
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Problem #{problem}"
 | 
					msgid "Problem #{problem}"
 | 
				
			||||||
msgstr "Problème n°{problem}"
 | 
					msgstr "Problème n°{problem}"
 | 
				
			||||||
@@ -911,8 +912,8 @@ msgstr "Êtes-vous sûr·e de vouloir annuler le tirage au sort ?"
 | 
				
			|||||||
msgid "Close"
 | 
					msgid "Close"
 | 
				
			||||||
msgstr "Fermer"
 | 
					msgstr "Fermer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: draw/views.py:31 participation/views.py:163 participation/views.py:509
 | 
					#: draw/views.py:31 participation/views.py:163 participation/views.py:512
 | 
				
			||||||
#: participation/views.py:540
 | 
					#: participation/views.py:543
 | 
				
			||||||
msgid "You are not in a team."
 | 
					msgid "You are not in a team."
 | 
				
			||||||
msgstr "Vous n'êtes pas dans une équipe."
 | 
					msgstr "Vous n'êtes pas dans une équipe."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1029,7 +1030,7 @@ msgstr "Ce trigramme est déjà utilisé."
 | 
				
			|||||||
msgid "No team was found with this access code."
 | 
					msgid "No team was found with this access code."
 | 
				
			||||||
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
 | 
					msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/forms.py:59 participation/views.py:511
 | 
					#: participation/forms.py:59 participation/views.py:514
 | 
				
			||||||
msgid "The team is already validated or the validation is pending."
 | 
					msgid "The team is already validated or the validation is pending."
 | 
				
			||||||
msgstr "La validation de l'équipe est déjà faite ou en cours."
 | 
					msgstr "La validation de l'équipe est déjà faite ou en cours."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1382,7 +1383,7 @@ msgstr "finale"
 | 
				
			|||||||
msgid "Google Sheet ID"
 | 
					msgid "Google Sheet ID"
 | 
				
			||||||
msgstr "ID de la feuille Google Sheets"
 | 
					msgstr "ID de la feuille Google Sheets"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:467 participation/views.py:1814
 | 
					#: participation/models.py:467 participation/views.py:1820
 | 
				
			||||||
msgid "Notation sheet"
 | 
					msgid "Notation sheet"
 | 
				
			||||||
msgstr "Feuille de notation"
 | 
					msgstr "Feuille de notation"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1392,7 +1393,8 @@ msgid "Final ranking"
 | 
				
			|||||||
msgstr "Classement final"
 | 
					msgstr "Classement final"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:487 participation/models.py:559
 | 
					#: participation/models.py:487 participation/models.py:559
 | 
				
			||||||
#: participation/models.py:1333 participation/views.py:1734
 | 
					#: participation/models.py:1333 participation/views.py:1740
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:58
 | 
				
			||||||
msgid "Team"
 | 
					msgid "Team"
 | 
				
			||||||
msgstr "Équipe"
 | 
					msgstr "Équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1425,14 +1427,14 @@ msgid "Tweaks day 3"
 | 
				
			|||||||
msgstr "Ajustements 3"
 | 
					msgstr "Ajustements 3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:491 participation/models.py:1333
 | 
					#: participation/models.py:491 participation/models.py:1333
 | 
				
			||||||
#: participation/views.py:1741
 | 
					#: participation/views.py:1747
 | 
				
			||||||
msgid "Total"
 | 
					msgid "Total"
 | 
				
			||||||
msgstr "Total"
 | 
					msgstr "Total"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:491 participation/models.py:559
 | 
					#: participation/models.py:491 participation/models.py:559
 | 
				
			||||||
#: participation/models.py:1333
 | 
					#: participation/models.py:1333
 | 
				
			||||||
#: participation/templates/participation/tournament_harmonize.html:14
 | 
					#: participation/templates/participation/tournament_harmonize.html:14
 | 
				
			||||||
#: participation/views.py:1744
 | 
					#: participation/views.py:1750
 | 
				
			||||||
msgid "Rank"
 | 
					msgid "Rank"
 | 
				
			||||||
msgstr "Rang"
 | 
					msgstr "Rang"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1443,7 +1445,7 @@ msgstr "Rang"
 | 
				
			|||||||
#: participation/models.py:1360 participation/models.py:1367
 | 
					#: participation/models.py:1360 participation/models.py:1367
 | 
				
			||||||
#: participation/models.py:1371 participation/models.py:1616
 | 
					#: participation/models.py:1371 participation/models.py:1616
 | 
				
			||||||
#: participation/models.py:1638 participation/models.py:2102
 | 
					#: participation/models.py:1638 participation/models.py:2102
 | 
				
			||||||
#: participation/views.py:1815
 | 
					#: participation/views.py:1821
 | 
				
			||||||
msgid "Pool"
 | 
					msgid "Pool"
 | 
				
			||||||
msgstr "Poule"
 | 
					msgstr "Poule"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1707,59 +1709,59 @@ msgid "The president of the jury must be part of the jury."
 | 
				
			|||||||
msgstr "Læ président⋅e du jury doit faire partie du jury."
 | 
					msgstr "Læ président⋅e du jury doit faire partie du jury."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1259 participation/models.py:1333
 | 
					#: participation/models.py:1259 participation/models.py:1333
 | 
				
			||||||
#: participation/views.py:1489 participation/views.py:1738
 | 
					#: participation/views.py:1495 participation/views.py:1744
 | 
				
			||||||
msgid "Problem"
 | 
					msgid "Problem"
 | 
				
			||||||
msgstr "Problème"
 | 
					msgstr "Problème"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1260 participation/views.py:1510
 | 
					#: participation/models.py:1260 participation/views.py:1516
 | 
				
			||||||
msgid "Reporter"
 | 
					msgid "Reporter"
 | 
				
			||||||
msgstr "Défenseur⋅se"
 | 
					msgstr "Défenseur⋅se"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1261 participation/views.py:1516
 | 
					#: participation/models.py:1261 participation/views.py:1522
 | 
				
			||||||
msgid "Opponent"
 | 
					msgid "Opponent"
 | 
				
			||||||
msgstr "Opposant⋅e"
 | 
					msgstr "Opposant⋅e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1262 participation/views.py:1523
 | 
					#: participation/models.py:1262 participation/views.py:1529
 | 
				
			||||||
msgid "Reviewer"
 | 
					msgid "Reviewer"
 | 
				
			||||||
msgstr "Rapporteur⋅rice"
 | 
					msgstr "Rapporteur⋅rice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1263 participation/views.py:1530
 | 
					#: participation/models.py:1263 participation/views.py:1536
 | 
				
			||||||
msgid "Observer"
 | 
					msgid "Observer"
 | 
				
			||||||
msgstr "Observateur⋅rice"
 | 
					msgstr "Observateur⋅rice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1264 participation/views.py:1504
 | 
					#: participation/models.py:1264 participation/views.py:1510
 | 
				
			||||||
msgid "Role"
 | 
					msgid "Role"
 | 
				
			||||||
msgstr "Rôle"
 | 
					msgstr "Rôle"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1265 participation/models.py:1267
 | 
					#: participation/models.py:1265 participation/models.py:1267
 | 
				
			||||||
#: participation/models.py:1268 participation/views.py:1546
 | 
					#: participation/models.py:1268 participation/views.py:1552
 | 
				
			||||||
#: participation/views.py:1554 participation/views.py:1562
 | 
					#: participation/views.py:1560 participation/views.py:1568
 | 
				
			||||||
#: participation/views.py:1572
 | 
					#: participation/views.py:1578
 | 
				
			||||||
msgid "Writing"
 | 
					msgid "Writing"
 | 
				
			||||||
msgstr "Écrit"
 | 
					msgstr "Écrit"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1266 participation/models.py:1267
 | 
					#: participation/models.py:1266 participation/models.py:1267
 | 
				
			||||||
#: participation/models.py:1268 participation/views.py:1550
 | 
					#: participation/models.py:1268 participation/views.py:1556
 | 
				
			||||||
#: participation/views.py:1558 participation/views.py:1567
 | 
					#: participation/views.py:1564 participation/views.py:1573
 | 
				
			||||||
#: participation/views.py:1576
 | 
					#: participation/views.py:1582
 | 
				
			||||||
msgid "Oral"
 | 
					msgid "Oral"
 | 
				
			||||||
msgstr "Oral"
 | 
					msgstr "Oral"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1269 participation/views.py:1538
 | 
					#: participation/models.py:1269 participation/views.py:1544
 | 
				
			||||||
#: participation/views.py:1539
 | 
					#: participation/views.py:1545
 | 
				
			||||||
msgid "Juree"
 | 
					msgid "Juree"
 | 
				
			||||||
msgstr "Juré⋅e"
 | 
					msgstr "Juré⋅e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1292 participation/models.py:1618
 | 
					#: participation/models.py:1292 participation/models.py:1618
 | 
				
			||||||
#: participation/models.py:1640 participation/views.py:1608
 | 
					#: participation/models.py:1640 participation/views.py:1614
 | 
				
			||||||
msgid "Average"
 | 
					msgid "Average"
 | 
				
			||||||
msgstr "Moyenne"
 | 
					msgstr "Moyenne"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1298 participation/views.py:1627
 | 
					#: participation/models.py:1298 participation/views.py:1633
 | 
				
			||||||
msgid "Coefficient"
 | 
					msgid "Coefficient"
 | 
				
			||||||
msgstr "Coefficien"
 | 
					msgstr "Coefficien"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/models.py:1299 participation/views.py:1670
 | 
					#: participation/models.py:1299 participation/views.py:1676
 | 
				
			||||||
msgid "Subtotal"
 | 
					msgid "Subtotal"
 | 
				
			||||||
msgstr "Sous-total"
 | 
					msgstr "Sous-total"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1972,12 +1974,15 @@ msgstr "Pas d'équipe définie"
 | 
				
			|||||||
#: registration/templates/registration/payment_form.html:210
 | 
					#: registration/templates/registration/payment_form.html:210
 | 
				
			||||||
#: registration/templates/registration/update_user.html:16
 | 
					#: registration/templates/registration/update_user.html:16
 | 
				
			||||||
#: registration/templates/registration/user_detail.html:220
 | 
					#: registration/templates/registration/user_detail.html:220
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:40
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:73
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_form.html:12
 | 
				
			||||||
msgid "Update"
 | 
					msgid "Update"
 | 
				
			||||||
msgstr "Modifier"
 | 
					msgstr "Modifier"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/templates/participation/create_team.html:11
 | 
					#: participation/templates/participation/create_team.html:11
 | 
				
			||||||
#: participation/templates/participation/tournament_form.html:14
 | 
					#: participation/templates/participation/tournament_form.html:14
 | 
				
			||||||
#: tfjm/templates/base.html:85
 | 
					#: survey/templates/survey/survey_form.html:14 tfjm/templates/base.html:85
 | 
				
			||||||
msgid "Create"
 | 
					msgid "Create"
 | 
				
			||||||
msgstr "Créer"
 | 
					msgstr "Créer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2305,6 +2310,7 @@ msgid "Back to pool detail"
 | 
				
			|||||||
msgstr "Retour aux détails de la poule"
 | 
					msgstr "Retour aux détails de la poule"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/templates/participation/team_detail.html:13
 | 
					#: participation/templates/participation/team_detail.html:13
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:16
 | 
				
			||||||
msgid "Name:"
 | 
					msgid "Name:"
 | 
				
			||||||
msgstr "Nom :"
 | 
					msgstr "Nom :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2465,7 +2471,7 @@ msgid "Invalidate"
 | 
				
			|||||||
msgstr "Invalider"
 | 
					msgstr "Invalider"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/templates/participation/team_detail.html:244
 | 
					#: participation/templates/participation/team_detail.html:244
 | 
				
			||||||
#: participation/views.py:341
 | 
					#: participation/views.py:344
 | 
				
			||||||
msgid "Upload motivation letter"
 | 
					msgid "Upload motivation letter"
 | 
				
			||||||
msgstr "Envoyer la lettre de motivation"
 | 
					msgstr "Envoyer la lettre de motivation"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2474,7 +2480,7 @@ msgid "Update team"
 | 
				
			|||||||
msgstr "Modifier l'équipe"
 | 
					msgstr "Modifier l'équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/templates/participation/team_detail.html:255
 | 
					#: participation/templates/participation/team_detail.html:255
 | 
				
			||||||
#: participation/views.py:503
 | 
					#: participation/views.py:506
 | 
				
			||||||
msgid "Leave team"
 | 
					msgid "Leave team"
 | 
				
			||||||
msgstr "Quitter l'équipe"
 | 
					msgstr "Quitter l'équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2736,7 +2742,7 @@ msgstr "Vous êtes déjà dans une équipe."
 | 
				
			|||||||
msgid "Join team"
 | 
					msgid "Join team"
 | 
				
			||||||
msgstr "Rejoindre une équipe"
 | 
					msgstr "Rejoindre une équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:164 participation/views.py:541
 | 
					#: participation/views.py:164 participation/views.py:544
 | 
				
			||||||
msgid "You don't participate, so you don't have any team."
 | 
					msgid "You don't participate, so you don't have any team."
 | 
				
			||||||
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
 | 
					msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2764,189 +2770,189 @@ msgstr ""
 | 
				
			|||||||
"d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre "
 | 
					"d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre "
 | 
				
			||||||
"de motivation, soit le tournoi n'a pas été choisi."
 | 
					"de motivation, soit le tournoi n'a pas été choisi."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:235
 | 
					#: participation/views.py:236
 | 
				
			||||||
msgid "Team validation"
 | 
					msgid "Team validation"
 | 
				
			||||||
msgstr "Validation d'équipe"
 | 
					msgstr "Validation d'équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:247
 | 
					#: participation/views.py:248
 | 
				
			||||||
msgid "You are not an organizer of the tournament."
 | 
					msgid "You are not an organizer of the tournament."
 | 
				
			||||||
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi."
 | 
					msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:250
 | 
					#: participation/views.py:251
 | 
				
			||||||
msgid "This team has no pending validation."
 | 
					msgid "This team has no pending validation."
 | 
				
			||||||
msgstr "L'équipe n'a pas de validation en attente."
 | 
					msgstr "L'équipe n'a pas de validation en attente."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:270
 | 
					#: participation/views.py:272
 | 
				
			||||||
msgid "Team validated"
 | 
					msgid "Team validated"
 | 
				
			||||||
msgstr "Équipe validée"
 | 
					msgstr "Équipe validée"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:279
 | 
					#: participation/views.py:282
 | 
				
			||||||
msgid "Team not validated"
 | 
					msgid "Team not validated"
 | 
				
			||||||
msgstr "Équipe non validée"
 | 
					msgstr "Équipe non validée"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:282
 | 
					#: participation/views.py:285
 | 
				
			||||||
msgid "You must specify if you validate the registration or not."
 | 
					msgid "You must specify if you validate the registration or not."
 | 
				
			||||||
msgstr "Vous devez spécifier si vous validez l'inscription ou non."
 | 
					msgstr "Vous devez spécifier si vous validez l'inscription ou non."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:318
 | 
					#: participation/views.py:321
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Update team {trigram}"
 | 
					msgid "Update team {trigram}"
 | 
				
			||||||
msgstr "Mise à jour de l'équipe {trigram}"
 | 
					msgstr "Mise à jour de l'équipe {trigram}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:380 participation/views.py:488
 | 
					#: participation/views.py:383 participation/views.py:491
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Motivation letter of {team}.{ext}"
 | 
					msgid "Motivation letter of {team}.{ext}"
 | 
				
			||||||
msgstr "Lettre de motivation de {team}.{ext}"
 | 
					msgstr "Lettre de motivation de {team}.{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:413
 | 
					#: participation/views.py:416
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Authorizations of team {trigram}.zip"
 | 
					msgid "Authorizations of team {trigram}.zip"
 | 
				
			||||||
msgstr "Autorisations de l'équipe {trigram}.zip"
 | 
					msgstr "Autorisations de l'équipe {trigram}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:417
 | 
					#: participation/views.py:420
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Authorizations of {tournament}.zip"
 | 
					msgid "Authorizations of {tournament}.zip"
 | 
				
			||||||
msgstr "Autorisations du tournoi {tournament}.zip"
 | 
					msgstr "Autorisations du tournoi {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:436
 | 
					#: participation/views.py:439
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Photo authorization of {participant}.{ext}"
 | 
					msgid "Photo authorization of {participant}.{ext}"
 | 
				
			||||||
msgstr "Autorisation de droit à l'image de {participant}.{ext}"
 | 
					msgstr "Autorisation de droit à l'image de {participant}.{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:445
 | 
					#: participation/views.py:448
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Parental authorization of {participant}.{ext}"
 | 
					msgid "Parental authorization of {participant}.{ext}"
 | 
				
			||||||
msgstr "Autorisation parentale de {participant}.{ext}"
 | 
					msgstr "Autorisation parentale de {participant}.{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:453
 | 
					#: participation/views.py:456
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Health sheet of {participant}.{ext}"
 | 
					msgid "Health sheet of {participant}.{ext}"
 | 
				
			||||||
msgstr "Fiche sanitaire de {participant}.{ext}"
 | 
					msgstr "Fiche sanitaire de {participant}.{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:461
 | 
					#: participation/views.py:464
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Vaccine sheet of {participant}.{ext}"
 | 
					msgid "Vaccine sheet of {participant}.{ext}"
 | 
				
			||||||
msgstr "Carnet de vaccination de {participant}.{ext}"
 | 
					msgstr "Carnet de vaccination de {participant}.{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:472
 | 
					#: participation/views.py:475
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Photo authorization of {participant} (final).{ext}"
 | 
					msgid "Photo authorization of {participant} (final).{ext}"
 | 
				
			||||||
msgstr "Autorisation de droit à l'image de {participant} (finale).{ext}"
 | 
					msgstr "Autorisation de droit à l'image de {participant} (finale).{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:481
 | 
					#: participation/views.py:484
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Parental authorization of {participant} (final).{ext}"
 | 
					msgid "Parental authorization of {participant} (final).{ext}"
 | 
				
			||||||
msgstr "Autorisation parentale de {participant} (finale).{ext}"
 | 
					msgstr "Autorisation parentale de {participant} (finale).{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:555
 | 
					#: participation/views.py:558
 | 
				
			||||||
msgid "The team is not validated yet."
 | 
					msgid "The team is not validated yet."
 | 
				
			||||||
msgstr "L'équipe n'est pas encore validée."
 | 
					msgstr "L'équipe n'est pas encore validée."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:569
 | 
					#: participation/views.py:572
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Participation of team {trigram}"
 | 
					msgid "Participation of team {trigram}"
 | 
				
			||||||
msgstr "Participation de l'équipe {trigram}"
 | 
					msgstr "Participation de l'équipe {trigram}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:658
 | 
					#: participation/views.py:661
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Payments of {tournament}"
 | 
					msgid "Payments of {tournament}"
 | 
				
			||||||
msgstr "Paiements de {tournament}"
 | 
					msgstr "Paiements de {tournament}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:758
 | 
					#: participation/views.py:761
 | 
				
			||||||
msgid "Notes published!"
 | 
					msgid "Notes published!"
 | 
				
			||||||
msgstr "Notes publiées !"
 | 
					msgstr "Notes publiées !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:760
 | 
					#: participation/views.py:763
 | 
				
			||||||
msgid "Notes hidden!"
 | 
					msgid "Notes hidden!"
 | 
				
			||||||
msgstr "Notes dissimulées !"
 | 
					msgstr "Notes dissimulées !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:791
 | 
					#: participation/views.py:794
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Harmonize notes of {tournament} - Day {round}"
 | 
					msgid "Harmonize notes of {tournament} - Day {round}"
 | 
				
			||||||
msgstr "Harmoniser les notes de {tournament} - Jour {round}"
 | 
					msgstr "Harmoniser les notes de {tournament} - Jour {round}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:905
 | 
					#: participation/views.py:908
 | 
				
			||||||
msgid "You can't upload a solution after the deadline."
 | 
					msgid "You can't upload a solution after the deadline."
 | 
				
			||||||
msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
 | 
					msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1025
 | 
					#: participation/views.py:1028
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Solutions of team {trigram}.zip"
 | 
					msgid "Solutions of team {trigram}.zip"
 | 
				
			||||||
msgstr "Solutions de l'équipe {trigram}.zip"
 | 
					msgstr "Solutions de l'équipe {trigram}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1026
 | 
					#: participation/views.py:1029
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Written reviews of team {trigram}.zip"
 | 
					msgid "Written reviews of team {trigram}.zip"
 | 
				
			||||||
msgstr "Notes de synthèse de l'équipe {trigram}.zip"
 | 
					msgstr "Notes de synthèse de l'équipe {trigram}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1043 participation/views.py:1059
 | 
					#: participation/views.py:1046 participation/views.py:1062
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Solutions of {tournament}.zip"
 | 
					msgid "Solutions of {tournament}.zip"
 | 
				
			||||||
msgstr "Solutions de {tournament}.zip"
 | 
					msgstr "Solutions de {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1044 participation/views.py:1060
 | 
					#: participation/views.py:1047 participation/views.py:1063
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Written reviews of {tournament}.zip"
 | 
					msgid "Written reviews of {tournament}.zip"
 | 
				
			||||||
msgstr "Notes de synthèse de {tournament}.zip"
 | 
					msgstr "Notes de synthèse de {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1069
 | 
					#: participation/views.py:1072
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Solutions for pool {pool} of tournament {tournament}.zip"
 | 
					msgid "Solutions for pool {pool} of tournament {tournament}.zip"
 | 
				
			||||||
msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip"
 | 
					msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1070
 | 
					#: participation/views.py:1073
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Written reviews for pool {pool} of tournament {tournament}.zip"
 | 
					msgid "Written reviews for pool {pool} of tournament {tournament}.zip"
 | 
				
			||||||
msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip"
 | 
					msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1112
 | 
					#: participation/views.py:1115
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Jury of pool {pool} for {tournament} with teams {teams}"
 | 
					msgid "Jury of pool {pool} for {tournament} with teams {teams}"
 | 
				
			||||||
msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
 | 
					msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1128
 | 
					#: participation/views.py:1131
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "The jury {name} is already in the pool!"
 | 
					msgid "The jury {name} is already in the pool!"
 | 
				
			||||||
msgstr "{name} est déjà dans la poule !"
 | 
					msgstr "{name} est déjà dans la poule !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1148
 | 
					#: participation/views.py:1151
 | 
				
			||||||
msgid "New jury account"
 | 
					msgid "New jury account"
 | 
				
			||||||
msgstr "Nouveau compte de juré⋅e"
 | 
					msgstr "Nouveau compte de juré⋅e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1169
 | 
					#: participation/views.py:1175
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "The jury {name} has been successfully added!"
 | 
					msgid "The jury {name} has been successfully added!"
 | 
				
			||||||
msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !"
 | 
					msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1205
 | 
					#: participation/views.py:1211
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "The jury {name} has been successfully removed!"
 | 
					msgid "The jury {name} has been successfully removed!"
 | 
				
			||||||
msgstr "{name} a été retiré⋅e avec succès du jury !"
 | 
					msgstr "{name} a été retiré⋅e avec succès du jury !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1231
 | 
					#: participation/views.py:1237
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "The jury {name} has been successfully promoted president!"
 | 
					msgid "The jury {name} has been successfully promoted president!"
 | 
				
			||||||
msgstr "{name} a été nommé⋅e président⋅e du jury !"
 | 
					msgstr "{name} a été nommé⋅e président⋅e du jury !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1259
 | 
					#: participation/views.py:1265
 | 
				
			||||||
msgid "The following user is not registered as a jury:"
 | 
					msgid "The following user is not registered as a jury:"
 | 
				
			||||||
msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :"
 | 
					msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1275
 | 
					#: participation/views.py:1281
 | 
				
			||||||
msgid "Notes were successfully uploaded."
 | 
					msgid "Notes were successfully uploaded."
 | 
				
			||||||
msgstr "Les notes ont bien été envoyées."
 | 
					msgstr "Les notes ont bien été envoyées."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1907
 | 
					#: participation/views.py:1912
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Notation sheets of pool {pool} of {tournament}.zip"
 | 
					msgid "Notation sheets of pool {pool} of {tournament}.zip"
 | 
				
			||||||
msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip"
 | 
					msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:1912
 | 
					#: participation/views.py:1917
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Notation sheets of {tournament}.zip"
 | 
					msgid "Notation sheets of {tournament}.zip"
 | 
				
			||||||
msgstr "Feuilles de notation de {tournament}.zip"
 | 
					msgstr "Feuilles de notation de {tournament}.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: participation/views.py:2079
 | 
					#: participation/views.py:2091
 | 
				
			||||||
msgid "You can't upload a written review after the deadline."
 | 
					msgid "You can't upload a written review after the deadline."
 | 
				
			||||||
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
 | 
					msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2981,7 +2987,7 @@ msgstr "Marquer comme invalide"
 | 
				
			|||||||
msgid "role"
 | 
					msgid "role"
 | 
				
			||||||
msgstr "rôle"
 | 
					msgstr "rôle"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/forms.py:25
 | 
					#: registration/forms.py:25 survey/templates/survey/survey_detail.html:50
 | 
				
			||||||
msgid "participant"
 | 
					msgid "participant"
 | 
				
			||||||
msgstr "participant⋅e"
 | 
					msgstr "participant⋅e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4200,22 +4206,22 @@ msgid "Impersonate"
 | 
				
			|||||||
msgstr "Impersonifier"
 | 
					msgstr "Impersonifier"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/templates/registration/user_detail.html:229
 | 
					#: registration/templates/registration/user_detail.html:229
 | 
				
			||||||
#: registration/views.py:333
 | 
					#: registration/views.py:334
 | 
				
			||||||
msgid "Upload photo authorization"
 | 
					msgid "Upload photo authorization"
 | 
				
			||||||
msgstr "Téléverser l'autorisation de droit à l'image"
 | 
					msgstr "Téléverser l'autorisation de droit à l'image"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/templates/registration/user_detail.html:236
 | 
					#: registration/templates/registration/user_detail.html:236
 | 
				
			||||||
#: registration/views.py:367
 | 
					#: registration/views.py:368
 | 
				
			||||||
msgid "Upload health sheet"
 | 
					msgid "Upload health sheet"
 | 
				
			||||||
msgstr "Téléverser la fiche sanitaire"
 | 
					msgstr "Téléverser la fiche sanitaire"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/templates/registration/user_detail.html:243
 | 
					#: registration/templates/registration/user_detail.html:243
 | 
				
			||||||
#: registration/views.py:388
 | 
					#: registration/views.py:389
 | 
				
			||||||
msgid "Upload vaccine sheet"
 | 
					msgid "Upload vaccine sheet"
 | 
				
			||||||
msgstr "Téléverser le carnet de vaccination"
 | 
					msgstr "Téléverser le carnet de vaccination"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/templates/registration/user_detail.html:249
 | 
					#: registration/templates/registration/user_detail.html:249
 | 
				
			||||||
#: registration/views.py:408
 | 
					#: registration/views.py:409
 | 
				
			||||||
msgid "Upload parental authorization"
 | 
					msgid "Upload parental authorization"
 | 
				
			||||||
msgstr "Téléverser l'autorisation parentale"
 | 
					msgstr "Téléverser l'autorisation parentale"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4242,36 +4248,36 @@ msgstr ""
 | 
				
			|||||||
"Les inscriptions pour cette année sont closes depuis le {closing_date:%d/%m/"
 | 
					"Les inscriptions pour cette année sont closes depuis le {closing_date:%d/%m/"
 | 
				
			||||||
"%Y %H:%M}."
 | 
					"%Y %H:%M}."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:140
 | 
					#: registration/views.py:141
 | 
				
			||||||
msgid "New organizer account"
 | 
					msgid "New organizer account"
 | 
				
			||||||
msgstr "Nouveau compte organisateur⋅rice"
 | 
					msgstr "Nouveau compte organisateur⋅rice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:166
 | 
					#: registration/views.py:167
 | 
				
			||||||
msgid "Email validation"
 | 
					msgid "Email validation"
 | 
				
			||||||
msgstr "Validation de l'adresse mail"
 | 
					msgstr "Validation de l'adresse mail"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:168
 | 
					#: registration/views.py:169
 | 
				
			||||||
msgid "Validate email"
 | 
					msgid "Validate email"
 | 
				
			||||||
msgstr "Valider l'adresse mail"
 | 
					msgstr "Valider l'adresse mail"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:207
 | 
					#: registration/views.py:208
 | 
				
			||||||
msgid "Email validation unsuccessful"
 | 
					msgid "Email validation unsuccessful"
 | 
				
			||||||
msgstr "Échec de la validation de l'adresse mail"
 | 
					msgstr "Échec de la validation de l'adresse mail"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:218
 | 
					#: registration/views.py:219
 | 
				
			||||||
msgid "Email validation email sent"
 | 
					msgid "Email validation email sent"
 | 
				
			||||||
msgstr "Mail de confirmation de l'adresse mail envoyé"
 | 
					msgstr "Mail de confirmation de l'adresse mail envoyé"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:226
 | 
					#: registration/views.py:227
 | 
				
			||||||
msgid "Resend email validation link"
 | 
					msgid "Resend email validation link"
 | 
				
			||||||
msgstr "Renvoyé le lien de validation de l'adresse mail"
 | 
					msgstr "Renvoyé le lien de validation de l'adresse mail"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:269
 | 
					#: registration/views.py:270
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Detail of user {user}"
 | 
					msgid "Detail of user {user}"
 | 
				
			||||||
msgstr "Détails de l'utilisateur⋅rice {user}"
 | 
					msgstr "Détails de l'utilisateur⋅rice {user}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: registration/views.py:294
 | 
					#: registration/views.py:295
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Update user {user}"
 | 
					msgid "Update user {user}"
 | 
				
			||||||
msgstr "Mise à jour de l'utilisateur⋅rice {user}"
 | 
					msgstr "Mise à jour de l'utilisateur⋅rice {user}"
 | 
				
			||||||
@@ -4358,6 +4364,122 @@ msgstr "Autorisation parentale de {student}.{ext}"
 | 
				
			|||||||
msgid "Payment receipt of {registrations}.{ext}"
 | 
					msgid "Payment receipt of {registrations}.{ext}"
 | 
				
			||||||
msgstr "Justificatif de paiement de {registrations}.{ext}"
 | 
					msgstr "Justificatif de paiement de {registrations}.{ext}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:21 survey/tables.py:14
 | 
				
			||||||
 | 
					msgid "survey identifier"
 | 
				
			||||||
 | 
					msgstr "identifiant du sondage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:22
 | 
				
			||||||
 | 
					msgid "The numeric identifier of the Limesurvey."
 | 
				
			||||||
 | 
					msgstr "L'identifiant numérique du Limesurvey."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:27
 | 
				
			||||||
 | 
					msgid "display name"
 | 
				
			||||||
 | 
					msgstr "nom affiché"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:32
 | 
				
			||||||
 | 
					msgid "invite whole team"
 | 
				
			||||||
 | 
					msgstr "inviter toute l'équipe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:33
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					"When this field is checked, teams will get only one survey invitation "
 | 
				
			||||||
 | 
					"instead of one per person."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Lorsque ce champ est coché, les équipes n'auront qu'une seule invitation au "
 | 
				
			||||||
 | 
					"sondage au lieu de une par personne."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:38
 | 
				
			||||||
 | 
					msgid "invite coaches"
 | 
				
			||||||
 | 
					msgstr "inviter les encadrant⋅es"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:39
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					"When this field is checked, coaches will also be invited in the survey. No "
 | 
				
			||||||
 | 
					"effect when the whole team is invited."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Lorsque ce champ est coché, les encadrant⋅es seront aussi invité⋅es au "
 | 
				
			||||||
 | 
					"sondage. Aucun effet lorsque toute l'équipe est invité."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:48
 | 
				
			||||||
 | 
					msgid "tournament restriction"
 | 
				
			||||||
 | 
					msgstr "restriction de tournoi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:49
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					"When this field is filled, the survey participants will be restricted to "
 | 
				
			||||||
 | 
					"this tournament members."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Lorsque ce champ est rempli, les participant⋅es au sondage seront "
 | 
				
			||||||
 | 
					"restreint⋅es aux membres de ce tournoi."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:55
 | 
				
			||||||
 | 
					msgid "participants that completed the survey"
 | 
				
			||||||
 | 
					msgstr "participant⋅es ayant complété le sondage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:61
 | 
				
			||||||
 | 
					msgid "teams that completed the survey"
 | 
				
			||||||
 | 
					msgstr "équipes ayant complété le sondage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:134 survey/templates/survey/survey_detail.html:10
 | 
				
			||||||
 | 
					msgid "survey"
 | 
				
			||||||
 | 
					msgstr "sondage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/models.py:135 tfjm/templates/navbar.html:78
 | 
				
			||||||
 | 
					msgid "surveys"
 | 
				
			||||||
 | 
					msgstr "sondages"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/tables.py:18 survey/templates/survey/survey_detail.html:51
 | 
				
			||||||
 | 
					msgid "completed"
 | 
				
			||||||
 | 
					msgstr "complété"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:19
 | 
				
			||||||
 | 
					msgid "One answer per team:"
 | 
				
			||||||
 | 
					msgstr "Une réponse par équipe :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:23
 | 
				
			||||||
 | 
					msgid "Coaches can answer the survey:"
 | 
				
			||||||
 | 
					msgstr "Les encadrant⋅es peuvent répondre au sondage :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:28
 | 
				
			||||||
 | 
					msgid "Tournament restriction:"
 | 
				
			||||||
 | 
					msgstr "Restriction de tournoi :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:32
 | 
				
			||||||
 | 
					msgid "Completion rate:"
 | 
				
			||||||
 | 
					msgstr "Taux de complétion :"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:41
 | 
				
			||||||
 | 
					msgid "Send invites"
 | 
				
			||||||
 | 
					msgstr "Envoyer les invitations"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:63
 | 
				
			||||||
 | 
					msgid "Yes"
 | 
				
			||||||
 | 
					msgstr "Oui"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:65
 | 
				
			||||||
 | 
					msgid "No"
 | 
				
			||||||
 | 
					msgstr "Non"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_detail.html:72
 | 
				
			||||||
 | 
					msgid "Update survey"
 | 
				
			||||||
 | 
					msgstr "Modifier le sondage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/templates/survey/survey_list.html:8
 | 
				
			||||||
 | 
					msgid "Add survey"
 | 
				
			||||||
 | 
					msgstr "Ajouter un sondage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/views.py:38
 | 
				
			||||||
 | 
					msgid "Invites sent!"
 | 
				
			||||||
 | 
					msgstr "Invitations envoyées !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/views.py:40
 | 
				
			||||||
 | 
					msgid "All invites were already sent."
 | 
				
			||||||
 | 
					msgstr "Toutes les invitations étaient déjà envoyés."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: survey/views.py:50
 | 
				
			||||||
 | 
					msgid "Completion data refreshed!"
 | 
				
			||||||
 | 
					msgstr "Données de complétion actualisées !"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/permissions.py:9
 | 
					#: tfjm/permissions.py:9
 | 
				
			||||||
msgid "Everyone, including anonymous users"
 | 
					msgid "Everyone, including anonymous users"
 | 
				
			||||||
msgstr "Tout le monde, incluant les utilisateur⋅rices anonymes"
 | 
					msgstr "Tout le monde, incluant les utilisateur⋅rices anonymes"
 | 
				
			||||||
@@ -4402,11 +4524,11 @@ msgstr "Privé, réservé aux utilisateur⋅rices explicitement autorisé⋅es"
 | 
				
			|||||||
msgid "Admin users"
 | 
					msgid "Admin users"
 | 
				
			||||||
msgstr "Administrateur⋅rices"
 | 
					msgstr "Administrateur⋅rices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/settings.py:174
 | 
					#: tfjm/settings.py:175
 | 
				
			||||||
msgid "English"
 | 
					msgid "English"
 | 
				
			||||||
msgstr "Anglais"
 | 
					msgstr "Anglais"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/settings.py:175
 | 
					#: tfjm/settings.py:176
 | 
				
			||||||
msgid "French"
 | 
					msgid "French"
 | 
				
			||||||
msgstr "Français"
 | 
					msgstr "Français"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4674,23 +4796,23 @@ msgstr "Mon équipe"
 | 
				
			|||||||
msgid "My participation"
 | 
					msgid "My participation"
 | 
				
			||||||
msgstr "Ma participation"
 | 
					msgstr "Ma participation"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/templates/navbar.html:78
 | 
					#: tfjm/templates/navbar.html:81
 | 
				
			||||||
msgid "Administration"
 | 
					msgid "Administration"
 | 
				
			||||||
msgstr "Administration"
 | 
					msgstr "Administration"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/templates/navbar.html:86
 | 
					#: tfjm/templates/navbar.html:89
 | 
				
			||||||
msgid "Search…"
 | 
					msgid "Search…"
 | 
				
			||||||
msgstr "Chercher…"
 | 
					msgstr "Chercher…"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/templates/navbar.html:95
 | 
					#: tfjm/templates/navbar.html:98
 | 
				
			||||||
msgid "Return to admin view"
 | 
					msgid "Return to admin view"
 | 
				
			||||||
msgstr "Retourner à l'interface administrateur⋅rice"
 | 
					msgstr "Retourner à l'interface administrateur⋅rice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/templates/navbar.html:102
 | 
					#: tfjm/templates/navbar.html:105
 | 
				
			||||||
msgid "Register"
 | 
					msgid "Register"
 | 
				
			||||||
msgstr "S'inscrire"
 | 
					msgstr "S'inscrire"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: tfjm/templates/navbar.html:119
 | 
					#: tfjm/templates/navbar.html:122
 | 
				
			||||||
msgid "My account"
 | 
					msgid "My account"
 | 
				
			||||||
msgstr "Mon compte"
 | 
					msgstr "Mon compte"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,7 +211,7 @@ class Team(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        :return: The mailing list to contact the team members.
 | 
					        :return: The mailing list to contact the team members.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return f"equipe-{slugify(self.trigram)}@{os.getenv('SYMPA_HOST', 'localhost')}"
 | 
					        return f"equipe-{slugify(self.trigram)}@{settings.SYMPA_HOST}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_mailing_list(self):
 | 
					    def create_mailing_list(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -392,21 +392,21 @@ class Tournament(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        :return: The mailing list to contact the team members.
 | 
					        :return: The mailing list to contact the team members.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return f"equipes-{slugify(self.name)}@{os.getenv('SYMPA_HOST', 'localhost')}"
 | 
					        return f"equipes-{slugify(self.name)}@{settings.SYMPA_HOST}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def organizers_email(self):
 | 
					    def organizers_email(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        :return: The mailing list to contact the team members.
 | 
					        :return: The mailing list to contact the team members.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return f"organisateurs-{slugify(self.name)}@{os.getenv('SYMPA_HOST', 'localhost')}"
 | 
					        return f"organisateurs-{slugify(self.name)}@{settings.SYMPA_HOST}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def jurys_email(self):
 | 
					    def jurys_email(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        :return: The mailing list to contact the team members.
 | 
					        :return: The mailing list to contact the team members.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return f"jurys-{slugify(self.name)}@{os.getenv('SYMPA_HOST', 'localhost')}"
 | 
					        return f"jurys-{slugify(self.name)}@{settings.SYMPA_HOST}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_mailing_lists(self):
 | 
					    def create_mailing_lists(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
channels[daphne]~=4.1.0
 | 
					channels[daphne]~=4.1.0
 | 
				
			||||||
channels-redis~=4.2.0
 | 
					channels-redis~=4.2.0
 | 
				
			||||||
 | 
					citric~=1.4.0
 | 
				
			||||||
crispy-bootstrap5~=2024.10
 | 
					crispy-bootstrap5~=2024.10
 | 
				
			||||||
Django>=5.1.2,<6.0
 | 
					Django>=5.1.2,<6.0
 | 
				
			||||||
django-crispy-forms~=2.3
 | 
					django-crispy-forms~=2.3
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								survey/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								survey/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										13
									
								
								survey/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								survey/admin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2025 by Animath
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .models import Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Survey)
 | 
				
			||||||
 | 
					class SurveyAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = ('survey_id', 'name', 'invite_team', 'invite_coaches', 'tournament',)
 | 
				
			||||||
 | 
					    list_filter = ('invite_team', 'invite_coaches', 'tournament',)
 | 
				
			||||||
 | 
					    search_fields = ('name',)
 | 
				
			||||||
							
								
								
									
										6
									
								
								survey/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								survey/apps.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyConfig(AppConfig):
 | 
				
			||||||
 | 
					    default_auto_field = "django.db.models.BigAutoField"
 | 
				
			||||||
 | 
					    name = "survey"
 | 
				
			||||||
							
								
								
									
										28
									
								
								survey/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								survey/forms.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .models import Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        if 'survey_id' in self.initial:
 | 
				
			||||||
 | 
					            self.fields['survey_id'].disabled = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Survey
 | 
				
			||||||
 | 
					        exclude = ('completed_registrations', 'completed_teams',)
 | 
				
			||||||
 | 
					        widgets = {
 | 
				
			||||||
 | 
					            'completed_registrations': forms.SelectMultiple(attrs={
 | 
				
			||||||
 | 
					                'class': 'selectpicker',
 | 
				
			||||||
 | 
					                'data-live-search': 'true',
 | 
				
			||||||
 | 
					                'data-live-search-normalize': 'true',
 | 
				
			||||||
 | 
					                'data-width': 'fit',
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            'completed_teams': forms.SelectMultiple(attrs={
 | 
				
			||||||
 | 
					                'class': 'selectpicker',
 | 
				
			||||||
 | 
					                'data-live-search': 'true',
 | 
				
			||||||
 | 
					                'data-live-search-normalize': 'true',
 | 
				
			||||||
 | 
					                'data-width': 'fit',
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
							
								
								
									
										13
									
								
								survey/management/commands/fetch_survey_completion_data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								survey/management/commands/fetch_survey_completion_data.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2025 by Animath
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.core.management import BaseCommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ...models import Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Command(BaseCommand):
 | 
				
			||||||
 | 
					    def handle(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        for survey in Survey.objects.all():
 | 
				
			||||||
 | 
					            survey.fetch_completion_data()
 | 
				
			||||||
							
								
								
									
										83
									
								
								survey/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								survey/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					# Generated by Django 5.1.5 on 2025-03-19 21:12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import django.db.models.deletion
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initial = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            "participation",
 | 
				
			||||||
 | 
					            "0023_tournament_unified_registration",
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        ("registration", "0014_participantregistration_country"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name="Survey",
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "survey_id",
 | 
				
			||||||
 | 
					                    models.IntegerField(
 | 
				
			||||||
 | 
					                        help_text="The numeric identifier of the Limesurvey.",
 | 
				
			||||||
 | 
					                        primary_key=True,
 | 
				
			||||||
 | 
					                        serialize=False,
 | 
				
			||||||
 | 
					                        verbose_name="survey identifier",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                ("name", models.CharField(max_length=255, verbose_name="display name")),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "invite_team",
 | 
				
			||||||
 | 
					                    models.BooleanField(
 | 
				
			||||||
 | 
					                        default=False,
 | 
				
			||||||
 | 
					                        help_text="When this field is checked, teams will get only one survey invitation instead of one per person.",
 | 
				
			||||||
 | 
					                        verbose_name="invite whole team",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "invite_coaches",
 | 
				
			||||||
 | 
					                    models.BooleanField(
 | 
				
			||||||
 | 
					                        default=True,
 | 
				
			||||||
 | 
					                        help_text="When this field is checked, coaches will also be invited in the survey. No effect when the whole team is invited.",
 | 
				
			||||||
 | 
					                        verbose_name="invite coaches",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "completed_registrations",
 | 
				
			||||||
 | 
					                    models.ManyToManyField(
 | 
				
			||||||
 | 
					                        related_name="completed_surveys",
 | 
				
			||||||
 | 
					                        to="registration.participantregistration",
 | 
				
			||||||
 | 
					                        verbose_name="participants that completed the survey",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "completed_teams",
 | 
				
			||||||
 | 
					                    models.ManyToManyField(
 | 
				
			||||||
 | 
					                        related_name="completed_surveys",
 | 
				
			||||||
 | 
					                        to="participation.team",
 | 
				
			||||||
 | 
					                        verbose_name="teams that completed the survey",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "tournament",
 | 
				
			||||||
 | 
					                    models.ForeignKey(
 | 
				
			||||||
 | 
					                        blank=True,
 | 
				
			||||||
 | 
					                        default=None,
 | 
				
			||||||
 | 
					                        help_text="When this field is filled, the survey participants will be restricted to this tournament members.",
 | 
				
			||||||
 | 
					                        null=True,
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.SET_NULL,
 | 
				
			||||||
 | 
					                        to="participation.tournament",
 | 
				
			||||||
 | 
					                        verbose_name="tournament restriction",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                "verbose_name": "survey",
 | 
				
			||||||
 | 
					                "verbose_name_plural": "surveys",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										0
									
								
								survey/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								survey/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										134
									
								
								survey/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								survey/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2025 by Animath
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from citric import Client
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from participation.models import Team, Tournament
 | 
				
			||||||
 | 
					from registration.models import ParticipantRegistration, StudentRegistration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Survey(models.Model):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ce modèle représente un sondage LimeSurvey afin de faciliter l'import des
 | 
				
			||||||
 | 
					    participant⋅es au sondage et d'effectuer le suivi.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    survey_id = models.IntegerField(
 | 
				
			||||||
 | 
					        primary_key=True,
 | 
				
			||||||
 | 
					        verbose_name=_("survey identifier"),
 | 
				
			||||||
 | 
					        help_text=_("The numeric identifier of the Limesurvey."),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = models.CharField(
 | 
				
			||||||
 | 
					        max_length=255,
 | 
				
			||||||
 | 
					        verbose_name=_("display name"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    invite_team = models.BooleanField(
 | 
				
			||||||
 | 
					        default=False,
 | 
				
			||||||
 | 
					        verbose_name=_("invite whole team"),
 | 
				
			||||||
 | 
					        help_text=_("When this field is checked, teams will get only one survey invitation instead of one per person."),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    invite_coaches = models.BooleanField(
 | 
				
			||||||
 | 
					        default=True,
 | 
				
			||||||
 | 
					        verbose_name=_("invite coaches"),
 | 
				
			||||||
 | 
					        help_text=_("When this field is checked, coaches will also be invited in the survey. No effect when the whole team is invited."),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tournament = models.ForeignKey(
 | 
				
			||||||
 | 
					        Tournament,
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        blank=True,
 | 
				
			||||||
 | 
					        default=None,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        verbose_name=_("tournament restriction"),
 | 
				
			||||||
 | 
					        help_text=_("When this field is filled, the survey participants will be restricted to this tournament members."),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    completed_registrations = models.ManyToManyField(
 | 
				
			||||||
 | 
					        ParticipantRegistration,
 | 
				
			||||||
 | 
					        related_name="completed_surveys",
 | 
				
			||||||
 | 
					        verbose_name=_("participants that completed the survey"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    completed_teams = models.ManyToManyField(
 | 
				
			||||||
 | 
					        Team,
 | 
				
			||||||
 | 
					        related_name="completed_surveys",
 | 
				
			||||||
 | 
					        verbose_name=_("teams that completed the survey"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def participants(self):
 | 
				
			||||||
 | 
					        if self.invite_team:
 | 
				
			||||||
 | 
					            teams = Team.objects.filter(participation__valid=True)
 | 
				
			||||||
 | 
					            if self.tournament:
 | 
				
			||||||
 | 
					                teams = teams.filter(participation__tournament=self.tournament)
 | 
				
			||||||
 | 
					            return teams.all()
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if self.invite_coaches:
 | 
				
			||||||
 | 
					                registrations = ParticipantRegistration.objects.filter(team__participation__valid=True)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                registrations = StudentRegistration.objects.filter(team__participation__valid=True)
 | 
				
			||||||
 | 
					            if self.tournament:
 | 
				
			||||||
 | 
					                registrations = registrations.filter(team__participation__tournament=self.tournament)
 | 
				
			||||||
 | 
					            return registrations.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def completed(self):
 | 
				
			||||||
 | 
					        if self.invite_team:
 | 
				
			||||||
 | 
					            return self.completed_teams
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return self.completed_registrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy("survey:survey_detail", args=(self.survey_id,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_participants_data(self):
 | 
				
			||||||
 | 
					        participants_data = []
 | 
				
			||||||
 | 
					        if self.invite_team:
 | 
				
			||||||
 | 
					            for team in self.participants:
 | 
				
			||||||
 | 
					                participant_data = {"firstname": team.name, "lastname": f"(équipe {team.trigram})", "email": team.email}
 | 
				
			||||||
 | 
					                participants_data.append(participant_data)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            for reg in self.participants:
 | 
				
			||||||
 | 
					                participant_data = {"firstname": reg.user.first_name, "lastname": reg.user.last_name, "email": reg.user.email}
 | 
				
			||||||
 | 
					                participants_data.append(participant_data)
 | 
				
			||||||
 | 
					        return participants_data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def invite_all(self):
 | 
				
			||||||
 | 
					        participants_data = self.generate_participants_data()
 | 
				
			||||||
 | 
					        with Client(f"{settings.LIMESURVEY_URL}/index.php/admin/remotecontrol", settings.LIMESURVEY_USER, settings.LIMESURVEY_PASSWORD) as client:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                current_participants = client.list_participants(self.survey_id, limit=10000)
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                current_participants = []
 | 
				
			||||||
 | 
					            current_participants_email = set(participant['participant_info']['email'] for participant in current_participants)
 | 
				
			||||||
 | 
					            participants_data = [participant_data for participant_data in participants_data if participant_data['email'] not in current_participants_email]
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                client.activate_tokens(self.survey_id)
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					            new_participants = client.add_participants(self.survey_id, participant_data=participants_data)
 | 
				
			||||||
 | 
					            if new_participants:
 | 
				
			||||||
 | 
					                client.invite_participants(self.survey_id, token_ids=[participant['tid'] for participant in new_participants])
 | 
				
			||||||
 | 
					            return new_participants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def fetch_completion_data(self):
 | 
				
			||||||
 | 
					        with Client(f"{settings.LIMESURVEY_URL}/index.php/admin/remotecontrol", settings.LIMESURVEY_USER, settings.LIMESURVEY_PASSWORD) as client:
 | 
				
			||||||
 | 
					            participants = client.list_participants(self.survey_id, limit=10000, attributes=['completed'])
 | 
				
			||||||
 | 
					        if self.invite_team:
 | 
				
			||||||
 | 
					            team_names = [participant['participant_info']['firstname'] for participant in participants if participant['completed'] != 'N']
 | 
				
			||||||
 | 
					            self.completed_teams.set(list(Team.objects.filter(name__in=team_names).values_list('id', flat=True)))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            mails = [participant['participant_info']['email'] for participant in participants if participant['completed'] != 'N']
 | 
				
			||||||
 | 
					            self.completed_registrations.set(list(ParticipantRegistration.objects.filter(user__email__in=mails).values_list('id', flat=True)))
 | 
				
			||||||
 | 
					        self.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("survey")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("surveys")
 | 
				
			||||||
							
								
								
									
										31
									
								
								survey/tables.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								survey/tables.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2025 by Animath
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					import django_tables2 as tables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .models import Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyTable(tables.Table):
 | 
				
			||||||
 | 
					    survey_id = tables.LinkColumn(
 | 
				
			||||||
 | 
					        'survey:survey_detail',
 | 
				
			||||||
 | 
					        args=[tables.A('survey_id')],
 | 
				
			||||||
 | 
					        verbose_name=lambda: _("survey identifier").capitalize(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nb_completed = tables.Column(
 | 
				
			||||||
 | 
					        verbose_name=_("completed").capitalize,
 | 
				
			||||||
 | 
					        accessor='survey_id'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_nb_completed(self, record):
 | 
				
			||||||
 | 
					        return f"{record.completed.count()}/{record.participants.count()}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        attrs = {
 | 
				
			||||||
 | 
					            'class': 'table table-condensed table-striped',
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        model = Survey
 | 
				
			||||||
 | 
					        fields = ('survey_id', 'name', 'invite_team', 'invite_coaches', 'tournament', 'nb_completed',)
 | 
				
			||||||
 | 
					        order_by = ('survey_id',)
 | 
				
			||||||
							
								
								
									
										84
									
								
								survey/templates/survey/survey_detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								survey/templates/survey/survey_detail.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					{% load crispy_forms_filters %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    <div class="card bg-body shadow">
 | 
				
			||||||
 | 
					        <div class="card-header text-center">
 | 
				
			||||||
 | 
					            <h4>
 | 
				
			||||||
 | 
					                {% trans "survey"|capfirst %} {{ survey.survey_id }}
 | 
				
			||||||
 | 
					                <a href="{{ TFJM.LIMESURVEY_URL }}/index.php/{{ survey.survey_id }}" target="_blank"><i class="fas fa-arrow-up-right-from-square"></i></a>
 | 
				
			||||||
 | 
					            </h4>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="card-body">
 | 
				
			||||||
 | 
					            <dl class="row">
 | 
				
			||||||
 | 
					                <dt class="col-sm-6 text-sm-end">{% trans "Name:" %}</dt>
 | 
				
			||||||
 | 
					                <dd class="col-sm-6">{{ survey.name }}</dd>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <dt class="col-sm-6 text-sm-end">{% trans "One answer per team:" %}</dt>
 | 
				
			||||||
 | 
					                <dd class="col-sm-6">{{ survey.invite_team|yesno }}</dd>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {% if not survey.invite_team %}
 | 
				
			||||||
 | 
					                    <dt class="col-sm-6 text-sm-end">{% trans "Coaches can answer the survey:" %}</dt>
 | 
				
			||||||
 | 
					                    <dd class="col-sm-6">{{ survey.invite_coaches|yesno }}</dd>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {% if survey.tournament %}
 | 
				
			||||||
 | 
					                    <dt class="col-sm-6 text-sm-end">{% trans "Tournament restriction:" %}</dt>
 | 
				
			||||||
 | 
					                    <dd class="col-sm-6">{{ survey.tournament }}</dd>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <dt class="col-sm-6 text-sm-end">{% trans "Completion rate:" %}</dt>
 | 
				
			||||||
 | 
					                <dd class="col-sm-6">
 | 
				
			||||||
 | 
					                    {{ survey.completed.count }}/{{ survey.participants.count }}
 | 
				
			||||||
 | 
					                    <a href="{% url "survey:survey_refresh_completed" pk=survey.pk %}"><i class="fas fa-arrow-rotate-right" alt="refresh"></i></a>
 | 
				
			||||||
 | 
					                </dd>
 | 
				
			||||||
 | 
					            </dl>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="card-footer text-center">
 | 
				
			||||||
 | 
					            <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updateSurveyModal">{% trans "Update" %}</button>
 | 
				
			||||||
 | 
					            <a class="btn btn-secondary" href="{% url "survey:survey_invite" pk=survey.pk %}">{% trans "Send invites" %}</a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <table class="table table-condensed table-striped">
 | 
				
			||||||
 | 
					        <thead>
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <th>{% trans "participant"|capfirst %}</th>
 | 
				
			||||||
 | 
					                <th>{% trans "completed"|capfirst %}</th>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					        </thead>
 | 
				
			||||||
 | 
					        <tbody>
 | 
				
			||||||
 | 
					            {% for participant in survey.participants %}
 | 
				
			||||||
 | 
					                <tr class="{% if participant in survey.completed.all %}table-success{% else %}table-danger{% endif %}">
 | 
				
			||||||
 | 
					                    {% if survey.invite_team %}
 | 
				
			||||||
 | 
					                        <td>{% trans "Team" %} {{ participant.name }} ({{ participant.trigram }})</td>
 | 
				
			||||||
 | 
					                    {% else %}
 | 
				
			||||||
 | 
					                        <td>{{ participant.user.first_name }} {{ participant.user.last_name }} ({% trans "team" %} {{ participant.team.trigram }})</td>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    {% if participant in survey.completed.all %}
 | 
				
			||||||
 | 
					                        <td>{% trans "Yes" %}</td>
 | 
				
			||||||
 | 
					                    {% else %}
 | 
				
			||||||
 | 
					                        <td>{% trans "No" %}</td>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					            {% endfor %}
 | 
				
			||||||
 | 
					        </tbody>
 | 
				
			||||||
 | 
					    </table>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% trans "Update survey" as modal_title %}
 | 
				
			||||||
 | 
					    {% trans "Update" as modal_button %}
 | 
				
			||||||
 | 
					    {% url "survey:survey_update" pk=survey.pk as modal_action %}
 | 
				
			||||||
 | 
					    {% include "base_modal.html" with modal_id="updateSurvey" %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block extrajavascript %}
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					        document.addEventListener('DOMContentLoaded', () => {
 | 
				
			||||||
 | 
					            initModal("updateSurvey", "{% url "survey:survey_update" pk=survey.pk %}")
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										17
									
								
								survey/templates/survey/survey_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								survey/templates/survey/survey_form.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{% extends request.content_only|yesno:"empty.html,base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load crispy_forms_filters i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    <form method="post">
 | 
				
			||||||
 | 
					        <div id="form-content">
 | 
				
			||||||
 | 
					            {% csrf_token %}
 | 
				
			||||||
 | 
					            {{ form|crispy }}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {% if object.pk %}
 | 
				
			||||||
 | 
					            <button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
 | 
				
			||||||
 | 
					        {% else %}
 | 
				
			||||||
 | 
					            <button class="btn btn-success" type="submit">{% trans "Create" %}</button>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					{% endblock content %}
 | 
				
			||||||
							
								
								
									
										14
									
								
								survey/templates/survey/survey_list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								survey/templates/survey/survey_list.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load django_tables2 i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    <div class="d-grid">
 | 
				
			||||||
 | 
					        <a href="{% url "survey:survey_create" %}" class="btn gap-0 btn-success">
 | 
				
			||||||
 | 
					            <i class="fas fa-square-poll-horizontal"></i> {% trans "Add survey" %}
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <hr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% render_table table %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										3
									
								
								survey/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								survey/tests.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										18
									
								
								survey/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								survey/urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2025 by Animath
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .views import SurveyCreateView, SurveyDetailView, SurveyInviteView, \
 | 
				
			||||||
 | 
					    SurveyListView, SurveyRefreshCompletedView, SurveyUpdateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_name = "survey"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = [
 | 
				
			||||||
 | 
					    path("", SurveyListView.as_view(), name="survey_list"),
 | 
				
			||||||
 | 
					    path("create/", SurveyCreateView.as_view(), name="survey_create"),
 | 
				
			||||||
 | 
					    path("<int:pk>/", SurveyDetailView.as_view(), name="survey_detail"),
 | 
				
			||||||
 | 
					    path("<int:pk>/invite/", SurveyInviteView.as_view(), name="survey_invite"),
 | 
				
			||||||
 | 
					    path("<int:pk>/refresh/", SurveyRefreshCompletedView.as_view(), name="survey_refresh_completed"),
 | 
				
			||||||
 | 
					    path("<int:pk>/update/", SurveyUpdateView.as_view(), name="survey_update"),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										56
									
								
								survey/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								survey/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2025 by Animath
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib import messages
 | 
				
			||||||
 | 
					from django.shortcuts import redirect
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from django.views.generic import CreateView, DetailView, UpdateView
 | 
				
			||||||
 | 
					from django_tables2 import SingleTableView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from tfjm.views import AdminMixin
 | 
				
			||||||
 | 
					from .forms import SurveyForm
 | 
				
			||||||
 | 
					from .models import Survey
 | 
				
			||||||
 | 
					from .tables import SurveyTable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyListView(AdminMixin, SingleTableView):
 | 
				
			||||||
 | 
					    model = Survey
 | 
				
			||||||
 | 
					    table_class = SurveyTable
 | 
				
			||||||
 | 
					    template_name = "survey/survey_list.html"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyCreateView(AdminMixin, CreateView):
 | 
				
			||||||
 | 
					    model = Survey
 | 
				
			||||||
 | 
					    form_class = SurveyForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyDetailView(AdminMixin, DetailView):
 | 
				
			||||||
 | 
					    model = Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyInviteView(AdminMixin, DetailView):
 | 
				
			||||||
 | 
					    model = Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        survey = self.get_object()
 | 
				
			||||||
 | 
					        new_participants = survey.invite_all()
 | 
				
			||||||
 | 
					        if new_participants:
 | 
				
			||||||
 | 
					            messages.success(request, _("Invites sent!"))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            messages.warning(request, _("All invites were already sent."))
 | 
				
			||||||
 | 
					        return redirect("survey:survey_detail", survey.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyRefreshCompletedView(AdminMixin, DetailView):
 | 
				
			||||||
 | 
					    model = Survey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        survey = self.get_object()
 | 
				
			||||||
 | 
					        survey.fetch_completion_data()
 | 
				
			||||||
 | 
					        messages.success(request, _("Completion data refreshed!"))
 | 
				
			||||||
 | 
					        return redirect("survey:survey_detail", survey.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SurveyUpdateView(AdminMixin, UpdateView):
 | 
				
			||||||
 | 
					    model = Survey
 | 
				
			||||||
 | 
					    form_class = SurveyForm
 | 
				
			||||||
@@ -19,5 +19,8 @@
 | 
				
			|||||||
# Update Google Drive notifications daily
 | 
					# Update Google Drive notifications daily
 | 
				
			||||||
0       0       *       *       *       cd /code && python manage.py renew_gdrive_notifications -v 0
 | 
					0       0       *       *       *       cd /code && python manage.py renew_gdrive_notifications -v 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Fetch LimeSurvey completion data
 | 
				
			||||||
 | 
					*/15    *       *       03-06   *       cd /code && python manage.py fetch_survey_completion_data -v 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Clean temporary files
 | 
					# Clean temporary files
 | 
				
			||||||
30      *       *       *       *       rm -rf /tmp/*
 | 
					30      *       *       *       *       rm -rf /tmp/*
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ def tfjm_context(request):
 | 
				
			|||||||
            'HAS_OBSERVER': settings.HAS_OBSERVER,
 | 
					            'HAS_OBSERVER': settings.HAS_OBSERVER,
 | 
				
			||||||
            'HAS_FINAL': settings.HAS_FINAL,
 | 
					            'HAS_FINAL': settings.HAS_FINAL,
 | 
				
			||||||
            'HOME_PAGE_LINK': settings.HOME_PAGE_LINK,
 | 
					            'HOME_PAGE_LINK': settings.HOME_PAGE_LINK,
 | 
				
			||||||
 | 
					            'LIMESURVEY_URL': settings.LIMESURVEY_URL,
 | 
				
			||||||
            'LOGO_PATH': "tfjm/img/" + settings.LOGO_FILE,
 | 
					            'LOGO_PATH': "tfjm/img/" + settings.LOGO_FILE,
 | 
				
			||||||
            'NB_ROUNDS': settings.NB_ROUNDS,
 | 
					            'NB_ROUNDS': settings.NB_ROUNDS,
 | 
				
			||||||
            'ML_MANAGEMENT': settings.ML_MANAGEMENT,
 | 
					            'ML_MANAGEMENT': settings.ML_MANAGEMENT,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,16 +3,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_client = None
 | 
					_client = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_sympa_client():
 | 
					def get_sympa_client():
 | 
				
			||||||
    global _client
 | 
					    global _client
 | 
				
			||||||
    if _client is None:
 | 
					    if _client is None:
 | 
				
			||||||
        if os.getenv("SYMPA_PASSWORD", None):  # pragma: no cover
 | 
					        if settings.SYMPA_PASSWORD is not None:  # pragma: no cover
 | 
				
			||||||
            from sympasoap import Client
 | 
					            from sympasoap import Client
 | 
				
			||||||
            _client = Client("https://" + os.getenv("SYMPA_URL"))
 | 
					            _client = Client("https://" + settings.SYMPA_URL)
 | 
				
			||||||
            _client.login(os.getenv("SYMPA_EMAIL"), os.getenv("SYMPA_PASSWORD"))
 | 
					            _client.login(settings.SYMPA_EMAIL, settings.SYMPA_PASSWORD)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            _client = FakeSympaSoapClient()
 | 
					            _client = FakeSympaSoapClient()
 | 
				
			||||||
    return _client
 | 
					    return _client
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,6 +74,7 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'draw',
 | 
					    'draw',
 | 
				
			||||||
    'registration',
 | 
					    'registration',
 | 
				
			||||||
    'participation',
 | 
					    'participation',
 | 
				
			||||||
 | 
					    'survey',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if "test" not in sys.argv:  # pragma: no cover
 | 
					if "test" not in sys.argv:  # pragma: no cover
 | 
				
			||||||
@@ -300,6 +301,12 @@ CHANNEL_LAYERS = {
 | 
				
			|||||||
PHONENUMBER_DB_FORMAT = 'NATIONAL'
 | 
					PHONENUMBER_DB_FORMAT = 'NATIONAL'
 | 
				
			||||||
PHONENUMBER_DEFAULT_REGION = 'FR'
 | 
					PHONENUMBER_DEFAULT_REGION = 'FR'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sympa configuration
 | 
				
			||||||
 | 
					SYMPA_HOST = os.getenv("SYMPA_HOST", "localhost")
 | 
				
			||||||
 | 
					SYMPA_URL = os.getenv("SYMPA_URL", "localhost")
 | 
				
			||||||
 | 
					SYMPA_EMAIL = os.getenv("SYMPA_EMAIL", "contact@localhost")
 | 
				
			||||||
 | 
					SYMPA_PASSWORD = os.getenv("SYMPA_PASSWORD", None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Hello Asso API creds
 | 
					# Hello Asso API creds
 | 
				
			||||||
HELLOASSO_CLIENT_ID = os.getenv('HELLOASSO_CLIENT_ID', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
					HELLOASSO_CLIENT_ID = os.getenv('HELLOASSO_CLIENT_ID', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
				
			||||||
HELLOASSO_CLIENT_SECRET = os.getenv('HELLOASSO_CLIENT_SECRET', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
					HELLOASSO_CLIENT_SECRET = os.getenv('HELLOASSO_CLIENT_SECRET', 'CHANGE_ME_IN_ENV_SETTINGS')
 | 
				
			||||||
@@ -322,6 +329,10 @@ GOOGLE_SERVICE_CLIENT = {
 | 
				
			|||||||
# The ID of the Google Drive folder where to store the notation sheets
 | 
					# The ID of the Google Drive folder where to store the notation sheets
 | 
				
			||||||
NOTES_DRIVE_FOLDER_ID = os.getenv("NOTES_DRIVE_FOLDER_ID", "CHANGE_ME_IN_ENV_SETTINGS")
 | 
					NOTES_DRIVE_FOLDER_ID = os.getenv("NOTES_DRIVE_FOLDER_ID", "CHANGE_ME_IN_ENV_SETTINGS")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIMESURVEY_URL = os.getenv("LIMESURVEY_URL", "https://survey.example.com")
 | 
				
			||||||
 | 
					LIMESURVEY_USER = os.getenv("LIMESURVEY_USER", "CHANGE_ME_IN_ENV_SETTINGS")
 | 
				
			||||||
 | 
					LIMESURVEY_PASSWORD = os.getenv("LIMESURVEY_PASSWORD", "CHANGE_ME_IN_ENV_SETTINGS")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Custom parameters
 | 
					# Custom parameters
 | 
				
			||||||
FORBIDDEN_TRIGRAMS = [
 | 
					FORBIDDEN_TRIGRAMS = [
 | 
				
			||||||
    "BIT",
 | 
					    "BIT",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,6 +74,9 @@
 | 
				
			|||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
            {% if user.registration.is_admin %}
 | 
					            {% if user.registration.is_admin %}
 | 
				
			||||||
 | 
					                <li class="nav-item active">
 | 
				
			||||||
 | 
					                    <a class="nav-link" href="{% url "survey:survey_list" %}"><i class="fas fa-square-poll-horizontal"></i> {% trans "surveys"|capfirst %}</a>
 | 
				
			||||||
 | 
					                </li>
 | 
				
			||||||
                <li class="nav-item active">
 | 
					                <li class="nav-item active">
 | 
				
			||||||
                    <a class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
 | 
					                    <a class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ urlpatterns = [
 | 
				
			|||||||
    path('draw/', include('draw.urls')),
 | 
					    path('draw/', include('draw.urls')),
 | 
				
			||||||
    path('participation/', include('participation.urls')),
 | 
					    path('participation/', include('participation.urls')),
 | 
				
			||||||
    path('registration/', include('registration.urls')),
 | 
					    path('registration/', include('registration.urls')),
 | 
				
			||||||
 | 
					    path('survey/', include('survey.urls')),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    path('media/authorization/photo/<str:filename>/', PhotoAuthorizationView.as_view(),
 | 
					    path('media/authorization/photo/<str:filename>/', PhotoAuthorizationView.as_view(),
 | 
				
			||||||
         name='photo_authorization'),
 | 
					         name='photo_authorization'),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user