# TP : Z-Event - La régie de données d'un marathon caritatif > **Thème** : Révisions Python - variables, fonctions, boucles, listes, dictionnaires --- ## Contexte Depuis 2016, le **Z-Event** réunit chaque année des dizaines de streamers francophones pendant plusieurs jours d'affilée, dans un même lieu, pour récolter des dons au profit d'une association. L'événement a été créé par **ZeratoR** et **Dach**. Le principe est simple : les spectateurs donnent, les streamers relèvent des défis, et un compteur géant affiche la cagnotte en direct. Derrière ce compteur, il y a du code. Chaque don qui tombe doit être enregistré, additionné, attribué à la chaîne sur laquelle il a été fait, comparé aux paliers annoncés, puis réaffiché en moins d'une seconde sur tous les écrans. C'est ce travail que vous allez faire ici. Vous êtes bénévole à la **régie de données** de l'événement. Votre mission : écrire les outils Python qui alimentent le compteur et les classements. > Les données de ce TP (montants, pseudos, horaires, cagnottes) sont **entièrement inventées** > pour l'exercice. Elles ne correspondent à aucune édition réelle du Z-Event. --- ## Objectifs pédagogiques - Manipuler des variables et reconnaître une erreur de type - Écrire des fonctions qui **renvoient** une valeur au lieu de l'afficher - Parcourir une liste avec une boucle et accumuler un résultat - Construire et exploiter des dictionnaires, y compris des dictionnaires de listes - Réinvestir ces notions dans les chapitres de Terminale : POO, structures linéaires, graphes Aucune de ces notions n'est nouvelle : tout a été vu en Première. L'objectif est de les remettre en service après la coupure estivale. --- ## Les données Copiez ce bloc en tête de votre fichier. Toutes les parties du TP s'appuient dessus. ```python # Toutes ces données sont inventées pour l'exercice. objectif = 2500000 collecte = 1837450.75 dons = [ {"pseudo": "Nyx_42", "streamer": "Antoine Daniel", "montant": 5.0, "heure": "18:04"}, {"pseudo": "TitiLeBg", "streamer": "MisterMV", "montant": 20.0, "heure": "18:11"}, {"pseudo": "Camille_R", "streamer": "Joueur du Grenier", "montant": 50.0, "heure": "18:23"}, {"pseudo": "Nyx_42", "streamer": "Baghera Jones", "montant": 15.5, "heure": "18:37"}, {"pseudo": "Poulpe3000", "streamer": "Horty", "montant": 2.0, "heure": "18:41"}, {"pseudo": "Anonyme", "streamer": "Ultia", "montant": 120.0, "heure": "18:52"}, {"pseudo": "Sam_du_59", "streamer": "Antoine Daniel", "montant": 10.0, "heure": "19:03"}, {"pseudo": "Camille_R", "streamer": "Sylvain Levy", "montant": 30.0, "heure": "19:14"}, {"pseudo": "Lulu_2007", "streamer": "ZeratoR", "montant": 8.5, "heure": "19:20"}, {"pseudo": "TitiLeBg", "streamer": "Etoiles", "montant": 45.0, "heure": "19:33"}, {"pseudo": "Marion_NSI", "streamer": "Joueur du Grenier", "montant": 75.0, "heure": "19:47"}, {"pseudo": "Poulpe3000", "streamer": "MisterMV", "montant": 12.0, "heure": "19:55"}, {"pseudo": "Anonyme", "streamer": "Domingo", "montant": 250.0, "heure": "20:08"}, {"pseudo": "Kevin_B", "streamer": "Baghera Jones", "montant": 5.0, "heure": "20:16"}, {"pseudo": "Nyx_42", "streamer": "Maghla", "montant": 60.0, "heure": "20:29"}, {"pseudo": "Sam_du_59", "streamer": "Horty", "montant": 3.5, "heure": "20:38"}, {"pseudo": "Marion_NSI", "streamer": "Antoine Daniel", "montant": 100.0, "heure": "20:44"}, {"pseudo": "Lulu_2007", "streamer": "Alphacast", "montant": 18.0, "heure": "20:57"}, {"pseudo": "Camille_R", "streamer": "Ultia", "montant": 40.0, "heure": "21:05"}, {"pseudo": "Kevin_B", "streamer": "Ponce", "montant": 22.5, "heure": "21:12"}, {"pseudo": "TitiLeBg", "streamer": "Joueur du Grenier", "montant": 15.0, "heure": "21:26"}, {"pseudo": "Anonyme", "streamer": "MisterMV", "montant": 500.0, "heure": "21:39"}, {"pseudo": "Poulpe3000", "streamer": "Sylvain Levy", "montant": 7.5, "heure": "21:48"}, {"pseudo": "Marion_NSI", "streamer": "Baghera Jones", "montant": 35.0, "heure": "21:59"}, ] ``` --- ## Partie 1 : Le compteur (variables et types) ### 1.1. État de la cagnotte **Question 1.1** : Calculez dans une variable `pourcentage` la part de l'objectif déjà atteinte, arrondie à deux décimales. ```python pourcentage = # À compléter print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à 73.5 % ``` **Question 1.2** : Calculez dans une variable `reste` la somme qu'il manque encore pour atteindre l'objectif. ```python reste = # À compléter print(f"Il manque {reste} euros") # Il manque 662549.25 euros ``` ### 1.2. Le piège du compteur Le compteur du site reçoit les montants sous forme de **chaînes de caractères**, parce qu'ils arrivent d'un formulaire web. Le code ci-dessous ne fait donc pas ce qu'on croit. **Question 1.3** : Exécutez ce code, observez le résultat, puis corrigez-le pour que `total` contienne bien la somme des deux dons. ```python don_1 = "25" don_2 = "40" total = don_1 + don_2 print(total) # affiche 2540 au lieu de 65 # Correction : total = # À compléter print(total) # 65 ``` --- ## Partie 2 : L'annonce des dons (fonctions) Un don qui tombe doit être annoncé à l'écran. Ces fonctions produisent les messages. ### 2.1. Formater un don **Question 2.1** : Écrivez la fonction `formater_don(pseudo, montant)` qui **renvoie** la chaîne d'annonce. Attention : elle doit la renvoyer, pas l'afficher. ```python def formater_don(pseudo, montant): # À compléter pass # Test print(formater_don("Nyx_42", 5.0)) # Nyx_42 vient de donner 5.0 euros print(formater_don("Marion_NSI", 100.0)) # Marion_NSI vient de donner 100.0 euros ``` ### 2.2. Repérer les gros dons La régie déclenche une animation spéciale au-delà d'un certain montant. **Question 2.2** : Écrivez `est_gros_don(montant, seuil)` qui renvoie `True` si le montant est **supérieur ou égal** au seuil, `False` sinon. Le seuil vaut 50 par défaut. ```python def est_gros_don(montant, seuil=50): # À compléter pass # Test print(est_gros_don(120.0)) # True print(est_gros_don(50.0)) # True print(est_gros_don(20.0)) # False print(est_gros_don(20.0, 10)) # True ``` ### 2.3. Les paliers Les organisateurs annoncent des paliers au fil de la collecte. | Cagnotte | Palier | |----------|--------| | moins de 500 000 | `"Départ"` | | de 500 000 à 999 999 | `"Palier 1"` | | de 1 000 000 à 1 999 999 | `"Palier 2"` | | 2 000 000 et plus | `"Objectif"` | **Question 2.3** : Écrivez `palier(total)` qui renvoie le nom du palier atteint. ```python def palier(total): # À compléter pass # Test print(palier(120000)) # Départ print(palier(750000)) # Palier 1 print(palier(1837450.75)) # Palier 2 print(palier(2500000)) # Objectif ``` ### 2.4. Le message complet **Question 2.4** : Écrivez `annonce_regie(pseudo, montant)` qui renvoie le message affiché en régie. Votre fonction **doit appeler** `formater_don` et `est_gros_don` : ne recopiez pas leur contenu. Le message se termine par `" [GROS DON]"` lorsque le montant déclenche l'animation. ```python def annonce_regie(pseudo, montant): # À compléter pass # Test print(annonce_regie("Poulpe3000", 2.0)) # Poulpe3000 vient de donner 2.0 euros print(annonce_regie("Anonyme", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON] ``` --- ## Partie 3 : L'historique des dons (boucles et listes) La liste `dons` contient les vingt-quatre derniers dons enregistrés. Chaque don est un dictionnaire à quatre clés : `pseudo`, `streamer`, `montant`, `heure`. ### 3.1. Le total collecté **Question 3.1** : Écrivez `total_dons(liste_dons)` qui renvoie la somme de tous les montants. Utilisez une boucle et un accumulateur, pas la fonction `sum`. ```python def total_dons(liste_dons): # À compléter pass # Test print(total_dons(dons)) # 1449.5 print(total_dons([])) # 0 ``` ### 3.2. Le don le plus élevé **Question 3.2** : Écrivez `plus_gros_don(liste_dons)` qui renvoie le **dictionnaire** du don le plus élevé. L'usage de `max` est interdit. On suppose la liste non vide. ```python def plus_gros_don(liste_dons): # À compléter pass # Test record = plus_gros_don(dons) print(record["pseudo"], record["montant"]) # Anonyme 500.0 ``` ### 3.3. Compter et filtrer **Question 3.3** : Écrivez `compter_gros_dons(liste_dons, seuil)` qui renvoie le nombre de dons supérieurs ou égaux au seuil. Réutilisez `est_gros_don`. ```python def compter_gros_dons(liste_dons, seuil=50): # À compléter pass # Test print(compter_gros_dons(dons)) # 7 print(compter_gros_dons(dons, 100)) # 4 ``` **Question 3.4** : Écrivez `donateurs(liste_dons)` qui renvoie la liste des pseudos **sans doublon**, dans l'ordre de leur premier don. ```python def donateurs(liste_dons): # À compléter pass # Test print(donateurs(dons)) # ['Nyx_42', 'TitiLeBg', 'Camille_R', 'Poulpe3000', 'Anonyme', 'Sam_du_59', 'Lulu_2007', 'Marion_NSI', 'Kevin_B'] ``` ### 3.4. Le bandeau des derniers dons Le site n'affiche que les tout derniers dons reçus. **Question 3.5** : Écrivez `derniers_dons(liste_dons, n)` qui renvoie les `n` derniers dons de la liste, en utilisant une **tranche**. ```python def derniers_dons(liste_dons, n): # À compléter pass # Test for don in derniers_dons(dons, 3): print(don["heure"], don["pseudo"]) # 21:39 Anonyme # 21:48 Poulpe3000 # 21:59 Marion_NSI ``` --- ## Partie 4 : Le classement des chaînes (dictionnaires) Chaque don est attribué à la chaîne sur laquelle il a été fait. La régie tient à jour la cagnotte de chaque streamer. ### 4.1. La cagnotte de chaque chaîne **Question 4.1** : Écrivez `cagnottes(liste_dons)` qui renvoie un dictionnaire associant à chaque streamer le total des dons reçus sur sa chaîne. ```python def cagnottes(liste_dons): # À compléter pass # Test totaux = cagnottes(dons) print(totaux["Antoine Daniel"]) # 115.0 print(totaux["Joueur du Grenier"]) # 140.0 print(len(totaux)) # 13 ``` ### 4.2. La chaîne en tête **Question 4.2** : Écrivez `meilleure_chaine(totaux)` qui renvoie le nom du streamer dont la cagnotte est la plus élevée. Rappel : `for cle in dictionnaire` parcourt les clés. ```python def meilleure_chaine(totaux): # À compléter pass # Test print(meilleure_chaine(cagnottes(dons))) # MisterMV ``` ### 4.3. Le détail des dons On veut maintenant, pour chaque streamer, la **liste** des montants reçus et plus seulement leur somme : un dictionnaire dont les valeurs sont des listes. **Question 4.3** : Écrivez `dons_par_chaine(liste_dons)` qui renvoie un dictionnaire associant à chaque streamer la liste des montants reçus, dans l'ordre chronologique. ```python def dons_par_chaine(liste_dons): # À compléter pass # Test detail = dons_par_chaine(dons) print(detail["MisterMV"]) # [20.0, 12.0, 500.0] print(detail["Baghera Jones"]) # [15.5, 5.0, 35.0] ``` ### 4.4. Le podium **Question 4.4** : Écrivez `podium(totaux)` qui renvoie la liste des trois premiers streamers, du plus gros total au plus petit. Indication : `sorted(totaux, key=..., reverse=True)` trie les clés selon un critère. ```python def podium(totaux): # À compléter pass # Test print(podium(cagnottes(dons))) # ['MisterMV', 'Domingo', 'Ultia'] ``` --- ## Partie 5 : Le planning et les raids (synthèse) Un **raid**, c'est lorsqu'un streamer envoie son public sur une autre chaîne à la fin de son passage. Cette partie combine tout ce qui précède. Copiez ce second bloc de données à la suite du premier. ```python planning = [ ("Antoine Daniel", 18, 20), ("MisterMV", 20, 22), ("Joueur du Grenier", 22, 24), ("Baghera Jones", 0, 3), ("Ultia", 3, 6), ] raids = { "Antoine Daniel": ["MisterMV"], "MisterMV": ["Joueur du Grenier", "Horty"], "Joueur du Grenier": ["Baghera Jones"], "Baghera Jones": ["Ultia"], "Ultia": [], "Horty": ["Sylvain Levy"], "Sylvain Levy": [], } ``` ### 5.1. Qui est à l'antenne Le planning est une liste de triplets `(streamer, heure_de_debut, heure_de_fin)`, en heures entières sur vingt-quatre heures. Attention au créneau de Baghera Jones : il commence à minuit, donc son heure de début est plus petite que celle de la personne qui la précède. **Question 5.1** : Écrivez `a_l_antenne(planning, heure)` qui renvoie le nom du streamer en direct à cette heure, ou `None` si personne ne l'est. L'heure de début est incluse, l'heure de fin est exclue. ```python def a_l_antenne(planning, heure): # À compléter pass # Test print(a_l_antenne(planning, 19)) # Antoine Daniel print(a_l_antenne(planning, 22)) # Joueur du Grenier print(a_l_antenne(planning, 1)) # Baghera Jones print(a_l_antenne(planning, 10)) # None ``` ### 5.2. Suivre la chaîne des raids Le dictionnaire `raids` associe à chaque streamer la liste de ceux vers qui il envoie son public. Certains n'envoient personne : leur liste est vide. **Question 5.2** : Écrivez `suivre_raids(raids, depart)` qui renvoie la liste des chaînes traversées en partant de `depart` et en suivant à chaque fois le **premier** raid, jusqu'à tomber sur un streamer qui n'en fait aucun. Le point de départ fait partie du résultat. ```python def suivre_raids(raids, depart): # À compléter pass # Test print(suivre_raids(raids, "Antoine Daniel")) # ['Antoine Daniel', 'MisterMV', 'Joueur du Grenier', 'Baghera Jones', 'Ultia'] print(suivre_raids(raids, "Horty")) # ['Horty', 'Sylvain Levy'] print(suivre_raids(raids, "Ultia")) # ['Ultia'] ``` --- ## Questions de synthèse Répondez en quelques lignes, sans machine. 1. À la question 1.3, `"25" + "40"` donne `"2540"`. Expliquez pourquoi Python ne signale aucune erreur alors que le résultat est faux du point de vue de la régie. 2. À la question 3.2, pourquoi initialise-t-on `record` avec le premier don de la liste plutôt qu'avec un montant de 0 ? 3. Aux questions 4.1 et 4.3, on parcourt la même liste de dons mais on ne construit pas la même structure. Dans quel cas le dictionnaire de listes est-il préférable au dictionnaire de totaux ? 4. La question 5.2 s'arrête lorsqu'un streamer ne fait aucun raid. Que se passerait-il si deux streamers se renvoyaient mutuellement leur public ? Comment modifieriez-vous la fonction pour l'éviter ? --- ## Ce que vous venez de réviser | Partie | Notion | Chapitre de Terminale concerné | |--------|--------|--------------------------------| | 1 | Variables, types, conversions | POO, structures linéaires | | 2 | Fonctions, `return`, paramètre par défaut | Récursivité, Modularité | | 3 | Boucles, accumulateur, listes, tranches | Parcours d'arbres et de graphes, tri fusion | | 4 | Dictionnaires, dictionnaires de listes | Graphes, programmation dynamique | | 5 | Synthèse, parcours d'un enchaînement | Graphes, Pile et File | La question 5.2 est un parcours de graphe : `raids` est une **liste d'adjacence**, exactement la structure que vous utiliserez au chapitre Graphes. --- ## Barème indicatif | Partie | Points | |--------|--------| | Partie 1 : Le compteur | 3 | | Partie 2 : L'annonce des dons | 5 | | Partie 3 : L'historique des dons | 5 | | Partie 4 : Le classement des chaînes | 4 | | Partie 5 : Le planning et les raids | 2 | | Questions de synthèse | 1 | | **Total** | **20** | --- Auteur : Florian Mathieu Licence CC BY-SA Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International.