Le rapport final
Des chiffres lisibles
Mama Brigitte ne lit pas de dictionnaires Python. Il lui faut une page claire, qu'elle peut lire, imprimer ou envoyer par WhatsApp. Dernière étape : présenter.
Deux outils de présentation pour commencer.
Séparer les milliers
142050 se lit mal ; 142 050 se lit tout de suite. Dans une f-string, {x:,} ajoute une virgule entre les milliers, à l'anglaise : f"{142050:,}" donne "142,050". En français, on sépare par une espace : il suffit de remplacer la virgule.
montant = 142050
print(f"{montant:,}".replace(",", " ")) # 142 050
Aligner en colonnes
print(f"{'riz':<10}|") # riz |
print(f"{34800:>8}|") # 34800|
Règle des tableaux bien faits : le texte s'aligne à gauche, les nombres à droite, pour que les unités, dizaines et centaines tombent les unes sous les autres.
Un petit tableau aligné
Lance, puis change les largeurs (10, 9) pour voir l'effet.
totaux = {"sucre": 40500, "riz": 34800, "spaghetti": 8800}
for produit, montant in totaux.items():
joli = f"{montant:,}".replace(",", " ")
print(f"{produit:<10}{joli:>9} FCFA")
Exercice : Brique : écrire un montant
Écris fcfa(montant) qui renvoie le montant avec une espace entre les milliers, suivi de FCFA. Par exemple fcfa(142050) renvoie "142 050 FCFA".
Voir une solution
def fcfa(montant):
"""Renvoie le montant avec les milliers séparés, suivi de FCFA."""
return f"{montant:,}".replace(",", " ") + " FCFA"Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Exercice : Brique : un tableau trié
Écris tableau(totaux) qui affiche une ligne par clé, de la plus grande valeur à la plus petite. Chaque ligne contient : la clé sur 12 caractères alignée à gauche, puis le montant (avec fcfa, qui est donnée) sur 14 caractères aligné à droite.
Pour {"Awa": 39150, "Paul": 64300, "Mireille": 38600}, l'affichage doit être :
Paul 64 300 FCFA
Awa 39 150 FCFA
Mireille 38 600 FCFA
Voir une solution
def fcfa(montant):
"""Renvoie le montant avec les milliers séparés, suivi de FCFA."""
return f"{montant:,}".replace(",", " ") + " FCFA"
def tableau(totaux):
"""Affiche les totaux du plus grand au plus petit, en colonnes alignées."""
for nom, montant in sorted(totaux.items(), key=lambda paire: paire[1], reverse=True):
print(f"{nom:<12}{fcfa(montant):>14}")
tableau({"Awa": 39150, "Paul": 64300, "Mireille": 38600})Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Écrire dans un fichier
Afficher le rapport, c'est bien. L'enregistrer dans un fichier qu'on peut envoyer, c'est mieux. On ouvre un fichier en écriture en ajoutant un deuxième argument à open :
with open("rapport.txt", "w", encoding="utf-8") as f:
f.write("RAPPORT DES VENTES\n")
f.write("Chiffre d'affaires : 142 050 FCFA\n")
Une technique pratique pour un rapport : construire d'abord la liste des lignes, puis l'afficher et l'écrire. Pour coller les lignes avec des retours à la ligne, on utilise .join :
lignes = ["RAPPORT", "Ventes : 42"]
texte = "\n".join(lignes)
print(texte)
with open("rapport.txt", "w", encoding="utf-8") as f:
f.write(texte + "\n")
Exercice : Brique : enregistrer le rapport
Écris enregistrer(lignes, nom_fichier) qui écrit toutes les lignes de la liste dans le fichier, une par ligne, avec un retour à la ligne à la fin de chacune.
Voir une solution
def enregistrer(lignes, nom_fichier):
"""Écrit les lignes dans le fichier, une par ligne."""
with open(nom_fichier, "w", encoding="utf-8") as f:
f.write("\n".join(lignes) + "\n")
enregistrer(["RAPPORT DES VENTES", "Ventes : 42"], "rapport.txt")
with open("rapport.txt", encoding="utf-8") as f:
print(f.read())Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Assembler le programme complet
Toutes les briques sont prêtes. Le programme final les assemble dans une fonction rapport(nom_fichier) qui :
- lit et nettoie le fichier avec
lire_ventes_propres; - calcule les indicateurs avec
chiffre_affaires,ca_paretmeilleur; - construit la liste des lignes du rapport ;
- l'affiche, l'enregistre dans
rapport.txt, et renvoie la liste.
Le rapport attendu, pour ventes.csv :
RAPPORT DES VENTES : ventes.csv
Ventes : 42 (0 ligne rejetée)
Chiffre d'affaires : 142 050 FCFA
Panier moyen : 3 382 FCFA
Meilleur produit : sucre
Meilleur vendeur : Paul
Meilleur mois : 2026-03
Par produit
sucre 40 500 FCFA 28.5 %
riz 34 800 FCFA 24.5 %
huile 28 500 FCFA 20.1 %
savon 21 000 FCFA 14.8 %
spaghetti 8 800 FCFA 6.2 %
lait 8 450 FCFA 5.9 %
Une ligne du tableau : le produit sur 12 caractères à gauche, le montant (fcfa) sur 14 à droite, puis la part {p:>7.1f} % (7 caractères à droite, 1 chiffre après la virgule). On peut combiner alignement et décimales dans la même accolade.
La ligne vide entre les indicateurs et le tableau est un texte vide "" dans la liste. Accorde « ligne rejetée » : au singulier pour 0 ou 1, au pluriel à partir de 2 (3 lignes rejetées).
Exercice : Le programme complet : rapport
Toutes les briques des cours précédents sont données. Écris rapport(nom_fichier), qui produit le rapport décrit à l'étape précédente : elle l'affiche, l'enregistre dans rapport.txt et renvoie la liste des lignes.
Le correcteur l'essaiera aussi sur le fichier d'avril, plein d'erreurs, où 3 lignes sont rejetées.
Voir une solution
def nettoyer_texte(texte):
"""Enlève les espaces autour et met en minuscules."""
return texte.strip().lower()
def convertir(texte):
"""Renvoie le nombre entier écrit dans texte, ou None s'il n'y en a pas."""
try:
return int(texte.replace(" ", ""))
except ValueError:
return None
def lire_ventes_propres(nom_fichier):
"""Lit un CSV de ventes, nettoie chaque ligne et met de côté les lignes invalides."""
with open(nom_fichier, encoding="utf-8") as f:
lignes = f.read().splitlines()
ventes = []
rejetees = []
for ligne in lignes[1:]:
if ligne.strip() == "":
continue
morceaux = ligne.split(",")
if len(morceaux) != 5:
rejetees.append(ligne)
continue
date, produit, quantite, prix, vendeur = morceaux
quantite = convertir(quantite)
prix = convertir(prix)
if quantite is None or prix is None:
rejetees.append(ligne)
continue
ventes.append({
"date": date.strip(),
"produit": nettoyer_texte(produit),
"quantite": quantite,
"prix": prix,
"vendeur": vendeur.strip(),
})
return ventes, rejetees
def chiffre_affaires(ventes):
"""Renvoie la somme de quantite x prix sur toutes les ventes."""
total = 0
for v in ventes:
total += v["quantite"] * v["prix"]
return total
def ca_par(ventes, cle):
"""Renvoie un dictionnaire : pour chaque valeur de la clé, son chiffre d'affaires."""
totaux = {}
for v in ventes:
totaux[v[cle]] = totaux.get(v[cle], 0) + v["quantite"] * v["prix"]
return totaux
def meilleur(totaux):
"""Renvoie la clé qui a la plus grande valeur."""
return max(totaux, key=totaux.get)
def panier_moyen(ventes):
"""Renvoie le montant moyen d'une vente, arrondi à l'entier."""
if len(ventes) == 0:
return 0
return round(chiffre_affaires(ventes) / len(ventes))
def fcfa(montant):
"""Renvoie le montant avec les milliers séparés, suivi de FCFA."""
return f"{montant:,}".replace(",", " ") + " FCFA"
def enregistrer(lignes, nom_fichier):
"""Écrit les lignes dans le fichier, une par ligne."""
with open(nom_fichier, "w", encoding="utf-8") as f:
f.write("\n".join(lignes) + "\n")
def rapport(nom_fichier):
"""Lit le fichier, calcule les indicateurs, affiche et enregistre le rapport."""
ventes, rejetees = lire_ventes_propres(nom_fichier)
ca = chiffre_affaires(ventes)
lignes = [f"RAPPORT DES VENTES : {nom_fichier}"]
produits = ca_par(ventes, "produit")
vendeurs = ca_par(ventes, "vendeur")
mois = {}
for v in ventes:
mois[v["date"][:7]] = mois.get(v["date"][:7], 0) + v["quantite"] * v["prix"]
mot = "ligne rejetée" if len(rejetees) <= 1 else "lignes rejetées"
lignes.append(f"Ventes : {len(ventes)} ({len(rejetees)} {mot})")
lignes.append(f"Chiffre d'affaires : {fcfa(ca)}")
lignes.append(f"Panier moyen : {fcfa(panier_moyen(ventes))}")
lignes.append(f"Meilleur produit : {meilleur(produits)}")
lignes.append(f"Meilleur vendeur : {meilleur(vendeurs)}")
lignes.append(f"Meilleur mois : {meilleur(mois)}")
lignes.append("")
lignes.append("Par produit")
for nom, montant in sorted(produits.items(), key=lambda paire: paire[1], reverse=True):
p = montant / ca * 100
lignes.append(f"{nom:<12}{fcfa(montant):>14}{p:>7.1f} %")
print("\n".join(lignes))
enregistrer(lignes, "rapport.txt")
return lignes
rapport("ventes.csv")Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Le mettre sur ton GitHub
Ce projet mérite d'être dans ton dépôt d'apprentissage ml-parcours : c'est un vrai programme, que n'importe qui peut lire et relancer. Sur ton PC, avec ce que tu as appris dans les modules Terminal et Git :
cd ml-parcours
mkdir projet-ventes
cd projet-ventes
- Crée
ventes.csvet colle les données (tu peux les copier depuis l'aperçu du fichier dans les exercices). - Crée
analyse.py, colle ton programme complet, et lance-le :python3 analyse.py. Le fichierrapport.txtapparaît dans le dossier. - Ajoute un
README.mdde trois lignes : ce que fait le programme, comment le lancer, un extrait du rapport. - Enregistre et publie :
git add .
git commit -m "Projet : analyse des ventes d'une boutique"
git push
Teste-toi
rapport.txt contient 20 lignes. On exécute with open("rapport.txt", "w") as f: f.write("Bonjour"). Que contient le fichier ensuite ?
- Les 20 lignes, puis Bonjour
- Seulement Bonjour (bonne réponse)
- Bonjour, puis les 20 lignes
- Une erreur, car le fichier existe déjà