863 lines
29 KiB
Python
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"))
|