# -*- coding: utf-8 -*- """Génère le TP Z-Event : énoncé Markdown, corrigé Markdown et notebook élève. Source unique : les trois fichiers sortent d'ici, ils ne peuvent pas diverger. Les sorties attendues affichées dans les commentaires (# Test) ne sont PAS écrites à la main : elles sont calculées en exécutant les solutions de référence au moment de la génération. """ import io, json, os, uuid DEST = os.path.dirname(os.path.abspath(__file__)) LICENCE = """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.""" # ---------------------------------------------------------------- LES DONNÉES DONNEES_SRC = '''# 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"}, ]''' PLANNING_SRC = '''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": [], }''' # Exécution des données pour calculer les sorties attendues. ENV = {} exec(DONNEES_SRC, ENV) exec(PLANNING_SRC, ENV) dons, planning, raids = ENV["dons"], ENV["planning"], ENV["raids"] objectif, collecte = ENV["objectif"], ENV["collecte"] pourcentage_att = round(collecte / objectif * 100, 2) reste_att = objectif - collecte total_att = 0 for d in dons: total_att += d["montant"] record_att = dons[0] for d in dons: if d["montant"] > record_att["montant"]: record_att = d gros50_att = len([d for d in dons if d["montant"] >= 50]) gros100_att = len([d for d in dons if d["montant"] >= 100]) donateurs_att = [] for d in dons: if d["pseudo"] not in donateurs_att: donateurs_att.append(d["pseudo"]) trois_derniers = dons[-3:] cagnottes_att = {} for d in dons: cagnottes_att[d["streamer"]] = cagnottes_att.get(d["streamer"], 0) + d["montant"] meilleure_att = max(cagnottes_att, key=lambda s: cagnottes_att[s]) podium_att = sorted(cagnottes_att, key=lambda s: cagnottes_att[s], reverse=True)[:3] detail_att = {} for d in dons: detail_att.setdefault(d["streamer"], []).append(d["montant"]) def antenne_att(h): for nom, debut, fin in planning: if debut < fin: if debut <= h < fin: return nom else: if h >= debut or h < fin: return nom return None def raids_att(depart): parcours = [depart] courant = depart while raids.get(courant): courant = raids[courant][0] parcours.append(courant) return parcours # ---------------------------------------------------------------- LES BLOCS BLOCS = [] def md(texte, dans_corrige=False): BLOCS.append(("md", texte, dans_corrige)) def code(source): BLOCS.append(("code", source, source)) def question(num, enonce, eleve, solution): BLOCS.append(("question", num, enonce, eleve, solution)) # ---------------------------------------------------------------- EN-TÊTE md("""# 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. """) code(DONNEES_SRC) # ---------------------------------------------------------------- PARTIE 1 md("""--- ## Partie 1 : Le compteur (variables et types) ### 1.1. État de la cagnotte """, dans_corrige=True) question( "1.1", """**Question 1.1** : Calculez dans une variable `pourcentage` la part de l'objectif déjà atteinte, arrondie à deux décimales.""", '''pourcentage = # À compléter print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à ''' + str(pourcentage_att) + " %", '''pourcentage = round(collecte / objectif * 100, 2) print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à ''' + str(pourcentage_att) + " %", ) question( "1.2", """**Question 1.2** : Calculez dans une variable `reste` la somme qu'il manque encore pour atteindre l'objectif.""", '''reste = # À compléter print(f"Il manque {reste} euros") # Il manque ''' + str(reste_att) + " euros", '''reste = objectif - collecte print(f"Il manque {reste} euros") # Il manque ''' + str(reste_att) + " euros", ) md("""### 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", """**Question 1.3** : Exécutez ce code, observez le résultat, puis corrigez-le pour que `total` contienne bien la somme des deux dons.""", '''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''', '''don_1 = "25" don_2 = "40" total = don_1 + don_2 print(total) # affiche 2540 : sur deux chaînes, + est une concaténation # Correction : convertir les chaînes en nombres avant d'additionner total = int(don_1) + int(don_2) print(total) # 65''', ) # ---------------------------------------------------------------- PARTIE 2 md("""--- ## 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 """, dans_corrige=True) question( "2.1", """**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.""", '''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''', '''def formater_don(pseudo, montant): return f"{pseudo} vient de donner {montant} euros" # 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''', ) md("""### 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", """**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.""", '''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''', '''def est_gros_don(montant, seuil=50): return montant >= seuil # 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''', ) md("""### 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", """**Question 2.3** : Écrivez `palier(total)` qui renvoie le nom du palier atteint.""", '''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''', '''def palier(total): if total < 500000: return "Départ" elif total < 1000000: return "Palier 1" elif total < 2000000: return "Palier 2" else: return "Objectif" # Test print(palier(120000)) # Départ print(palier(750000)) # Palier 1 print(palier(1837450.75)) # Palier 2 print(palier(2500000)) # Objectif''', ) md("""### 2.4. Le message complet """) question( "2.4", """**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.""", '''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]''', '''def annonce_regie(pseudo, montant): message = formater_don(pseudo, montant) if est_gros_don(montant): message = message + " [GROS DON]" return message # 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 md("""--- ## 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é """, dans_corrige=True) question( "3.1", """**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`.""", '''def total_dons(liste_dons): # À compléter pass # Test print(total_dons(dons)) # ''' + str(total_att) + ''' print(total_dons([])) # 0''', '''def total_dons(liste_dons): total = 0 for don in liste_dons: total = total + don["montant"] return total # Test print(total_dons(dons)) # ''' + str(total_att) + ''' print(total_dons([])) # 0''', ) md("""### 3.2. Le don le plus élevé """) question( "3.2", """**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.""", '''def plus_gros_don(liste_dons): # À compléter pass # Test record = plus_gros_don(dons) print(record["pseudo"], record["montant"]) # ''' + record_att["pseudo"] + " " + str(record_att["montant"]), '''def plus_gros_don(liste_dons): record = liste_dons[0] # on part du premier don, surtout pas de 0 for don in liste_dons: if don["montant"] > record["montant"]: record = don return record # Test record = plus_gros_don(dons) print(record["pseudo"], record["montant"]) # ''' + record_att["pseudo"] + " " + str(record_att["montant"]), ) md("""### 3.3. Compter et filtrer """) question( "3.3", """**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`.""", '''def compter_gros_dons(liste_dons, seuil=50): # À compléter pass # Test print(compter_gros_dons(dons)) # ''' + str(gros50_att) + ''' print(compter_gros_dons(dons, 100)) # ''' + str(gros100_att), '''def compter_gros_dons(liste_dons, seuil=50): compteur = 0 for don in liste_dons: if est_gros_don(don["montant"], seuil): compteur = compteur + 1 return compteur # Test print(compter_gros_dons(dons)) # ''' + str(gros50_att) + ''' print(compter_gros_dons(dons, 100)) # ''' + str(gros100_att), ) question( "3.4", """**Question 3.4** : Écrivez `donateurs(liste_dons)` qui renvoie la liste des pseudos **sans doublon**, dans l'ordre de leur premier don.""", '''def donateurs(liste_dons): # À compléter pass # Test print(donateurs(dons)) # ''' + str(donateurs_att), '''def donateurs(liste_dons): pseudos = [] for don in liste_dons: if don["pseudo"] not in pseudos: pseudos.append(don["pseudo"]) return pseudos # Test print(donateurs(dons)) # ''' + str(donateurs_att), ) md("""### 3.4. Le bandeau des derniers dons Le site n'affiche que les tout derniers dons reçus. """) question( "3.5", """**Question 3.5** : Écrivez `derniers_dons(liste_dons, n)` qui renvoie les `n` derniers dons de la liste, en utilisant une **tranche**.""", '''def derniers_dons(liste_dons, n): # À compléter pass # Test for don in derniers_dons(dons, 3): print(don["heure"], don["pseudo"]) # ''' + "\n# ".join("%s %s" % (d["heure"], d["pseudo"]) for d in trois_derniers), '''def derniers_dons(liste_dons, n): return liste_dons[-n:] # Test for don in derniers_dons(dons, 3): print(don["heure"], don["pseudo"]) # ''' + "\n# ".join("%s %s" % (d["heure"], d["pseudo"]) for d in trois_derniers), ) # ---------------------------------------------------------------- PARTIE 4 md("""--- ## 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 """, dans_corrige=True) question( "4.1", """**Question 4.1** : Écrivez `cagnottes(liste_dons)` qui renvoie un dictionnaire associant à chaque streamer le total des dons reçus sur sa chaîne.""", '''def cagnottes(liste_dons): # À compléter pass # Test totaux = cagnottes(dons) print(totaux["Antoine Daniel"]) # ''' + str(cagnottes_att["Antoine Daniel"]) + ''' print(totaux["Joueur du Grenier"]) # ''' + str(cagnottes_att["Joueur du Grenier"]) + ''' print(len(totaux)) # ''' + str(len(cagnottes_att)), '''def cagnottes(liste_dons): totaux = {} for don in liste_dons: streamer = don["streamer"] if streamer in totaux: totaux[streamer] = totaux[streamer] + don["montant"] else: totaux[streamer] = don["montant"] return totaux # Test totaux = cagnottes(dons) print(totaux["Antoine Daniel"]) # ''' + str(cagnottes_att["Antoine Daniel"]) + ''' print(totaux["Joueur du Grenier"]) # ''' + str(cagnottes_att["Joueur du Grenier"]) + ''' print(len(totaux)) # ''' + str(len(cagnottes_att)), ) md("""### 4.2. La chaîne en tête """) question( "4.2", """**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.""", '''def meilleure_chaine(totaux): # À compléter pass # Test print(meilleure_chaine(cagnottes(dons))) # ''' + meilleure_att, '''def meilleure_chaine(totaux): meilleur = None for streamer in totaux: if meilleur is None or totaux[streamer] > totaux[meilleur]: meilleur = streamer return meilleur # Test print(meilleure_chaine(cagnottes(dons))) # ''' + meilleure_att, ) md("""### 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", """**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.""", '''def dons_par_chaine(liste_dons): # À compléter pass # Test detail = dons_par_chaine(dons) print(detail["MisterMV"]) # ''' + str(detail_att["MisterMV"]) + ''' print(detail["Baghera Jones"]) # ''' + str(detail_att["Baghera Jones"]), '''def dons_par_chaine(liste_dons): detail = {} for don in liste_dons: streamer = don["streamer"] if streamer not in detail: detail[streamer] = [] detail[streamer].append(don["montant"]) return detail # Test detail = dons_par_chaine(dons) print(detail["MisterMV"]) # ''' + str(detail_att["MisterMV"]) + ''' print(detail["Baghera Jones"]) # ''' + str(detail_att["Baghera Jones"]), ) md("""### 4.4. Le podium """) question( "4.4", """**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.""", '''def podium(totaux): # À compléter pass # Test print(podium(cagnottes(dons))) # ''' + str(podium_att), '''def podium(totaux): classement = sorted(totaux, key=lambda streamer: totaux[streamer], reverse=True) return classement[:3] # Test print(podium(cagnottes(dons))) # ''' + str(podium_att), ) # ---------------------------------------------------------------- PARTIE 5 md("""--- ## 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. """, dans_corrige=True) code(PLANNING_SRC) md("""### 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", """**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.""", '''def a_l_antenne(planning, heure): # À compléter pass # Test print(a_l_antenne(planning, 19)) # ''' + str(antenne_att(19)) + ''' print(a_l_antenne(planning, 22)) # ''' + str(antenne_att(22)) + ''' print(a_l_antenne(planning, 1)) # ''' + str(antenne_att(1)) + ''' print(a_l_antenne(planning, 10)) # ''' + str(antenne_att(10)), '''def a_l_antenne(planning, heure): for streamer, debut, fin in planning: if debut < fin: if debut <= heure < fin: return streamer else: # créneau à cheval sur minuit if heure >= debut or heure < fin: return streamer return None # Test print(a_l_antenne(planning, 19)) # ''' + str(antenne_att(19)) + ''' print(a_l_antenne(planning, 22)) # ''' + str(antenne_att(22)) + ''' print(a_l_antenne(planning, 1)) # ''' + str(antenne_att(1)) + ''' print(a_l_antenne(planning, 10)) # ''' + str(antenne_att(10)), ) md("""### 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", """**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.""", '''def suivre_raids(raids, depart): # À compléter pass # Test print(suivre_raids(raids, "Antoine Daniel")) # ''' + str(raids_att("Antoine Daniel")) + ''' print(suivre_raids(raids, "Horty")) # ''' + str(raids_att("Horty")) + ''' print(suivre_raids(raids, "Ultia")) # ''' + str(raids_att("Ultia")), '''def suivre_raids(raids, depart): parcours = [depart] courant = depart while raids.get(courant): # .get évite l'erreur si la clé est absente courant = raids[courant][0] parcours.append(courant) return parcours # Test print(suivre_raids(raids, "Antoine Daniel")) # ''' + str(raids_att("Antoine Daniel")) + ''' print(suivre_raids(raids, "Horty")) # ''' + str(raids_att("Horty")) + ''' print(suivre_raids(raids, "Ultia")) # ''' + str(raids_att("Ultia")), ) # ---------------------------------------------------------------- FIN md("""--- ## 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** | --- """ + LICENCE) # ---------------------------------------------------------------- SORTIES def enonce_md(): parts = [] for b in BLOCS: if b[0] == "md": parts.append(b[1].strip("\n")) elif b[0] == "code": parts.append("```python\n" + b[1] + "\n```") else: parts.append(b[2].strip("\n") + "\n\n```python\n" + b[3] + "\n```") return "\n\n".join(parts) + "\n" def corrige_md(): parts = ["# Corrigé du TP Z-Event", "---"] exclus = ("## Contexte", "## Objectifs pédagogiques", "## Les données", "## Questions de synthèse", "## Ce que vous venez de réviser", "## Barème indicatif") for b in BLOCS: if b[0] == "md": # On reprend toute la hiérarchie de titres de l'énoncé (parties et # sous-parties), sauf le titre du TP et les sections de clôture. titres = [l for l in b[1].split("\n") if l.startswith("## ") or l.startswith("### ")] titres = [l for l in titres if l not in exclus] if titres: parts.append("\n\n".join(titres)) elif b[0] == "question": parts.append("**Question %s** :\n\n```python\n%s\n```" % (b[1], b[4])) complet = [DONNEES_SRC, PLANNING_SRC] + [b[4] for b in BLOCS if b[0] == "question"] parts.append("---") parts.append("## Code complet fonctionnel") parts.append("```python\n" + "\n\n".join(complet) + "\n```") parts.append("---") parts.append(LICENCE) return "\n\n".join(parts) + "\n" def notebook(): cells = [] def add(kind, src): c = {"cell_type": kind, "id": uuid.uuid4().hex[:8], "metadata": {}, "source": src.splitlines(keepends=True)} if kind == "code": c["execution_count"] = None c["outputs"] = [] cells.append(c) for b in BLOCS: if b[0] == "md": add("markdown", b[1].strip("\n")) elif b[0] == "code": add("code", b[1]) else: add("markdown", b[2].strip("\n")) add("code", b[3]) return {"cells": cells, "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"name": "python", "version": "3.11"}}, "nbformat": 4, "nbformat_minor": 5} io.open(os.path.join(DEST, "TP_ZEvent.md"), "w", encoding="utf-8").write(enonce_md()) io.open(os.path.join(DEST, "Corrige_TP_ZEvent.md"), "w", encoding="utf-8").write(corrige_md()) with io.open(os.path.join(DEST, "TP_ZEvent.ipynb"), "w", encoding="utf-8") as f: json.dump(notebook(), f, ensure_ascii=False, indent=1) print("écrits : TP_ZEvent.md, Corrige_TP_ZEvent.md, TP_ZEvent.ipynb (%d questions)" % sum(1 for b in BLOCS if b[0] == "question"))