Files
TermNSI/Revisions_Rentree/_generateur_zevent.py
T
2026-09-07 01:03:32 +02:00

863 lines
29 KiB
Python

# -*- 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
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>."""
# ---------------------------------------------------------------- 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"))