Ajouter la conversion des montants
Objectif : Associer le taux récupéré par API aux dépenses et calculer leur total dans une devise cible.
Vous savez déjà importer et valider un relevé, enregistrer les dépenses en JSON et récupérer un taux auprès de l’API Frankfurter. Il reste à relier ces éléments : appliquer le taux aux montants, additionner les valeurs converties et présenter un résultat lisible sans arrondir prématurément les calculs.
Vous allez compléter le parcours du projet sans déplacer les responsabilités : `change.py` fournit le taux, `transactions.py` calcule, et `main.py` orchestre. Les fonctions de lecture et de validation CSV, ainsi que l’écriture JSON, restent celles déjà mises en place dans les leçons précédentes.
Calculer un total converti
Le taux exprime combien d’unités de devise cible correspondent à une unité de devise source. Pour chaque dépense, multipliez donc `montant` par ce taux, puis additionnez les montants convertis. Le calcul ne doit pas arrondir chaque transaction : conserver les valeurs complètes jusqu’au total évite que des écarts d’arrondi se cumulent.
Ajoutez cette fonction à `depenses/transactions.py`, en conservant les fonctions métier déjà présentes dans ce module, notamment celles qui calculent le total en euros et valident une dépense. Elle applique le même contrat de données : chaque dépense est un dictionnaire contenant une valeur numérique sous la clé `montant`.
def calculer_total_converti(depenses, taux):
"""Retourne le total des dépenses converti avec le taux fourni."""
if not isinstance(taux, (int, float)) or taux <= 0:
raise ValueError("Le taux de conversion doit être un nombre positif.")
total = 0.0
for depense in depenses:
montant = depense["montant"]
if not isinstance(montant, (int, float)):
raise TypeError("Chaque montant doit être un nombre.")
total += montant * taux
return totalLa fonction refuse un taux nul, négatif ou non numérique plutôt que de produire un résultat trompeur. Si une dépense ne respecte pas le contrat attendu, l’erreur remonte à l’appelant ; vous ne la masquez pas dans la fonction de calcul. Les validations détaillées des dépenses importées sont déjà prises en charge par le parcours CSV existant.
Relier le taux au point d’entrée
Le module `depenses.change` possède déjà la fonction qui interroge l’API et signale les pannes avec `ErreurTaux`. Réutilisez cette fonction plutôt que de construire une seconde requête dans `main.py`. L’exemple ci-dessous montre le fichier complet du point d’entrée après évolution ; il reprend l’import CSV et la sauvegarde JSON du parcours assemblé.
import argparse
import sys
from depenses.change import ErreurTaux, obtenir_taux
from depenses.fichiers import lire_releve_csv, sauvegarder_json
from depenses.transactions import calculer_total_converti
def main():
parser = argparse.ArgumentParser(
description="Importe un relevé et calcule un total converti."
)
parser.add_argument("fichier_csv", help="Chemin du relevé CSV à importer")
parser.add_argument("--base", default="EUR", help="Devise des dépenses")
parser.add_argument("--cible", default="USD", help="Devise du total affiché")
args = parser.parse_args()
try:
depenses = lire_releve_csv(args.fichier_csv)
sauvegarder_json(depenses, "depenses.json")
taux = obtenir_taux(args.base, args.cible)
total = calculer_total_converti(depenses, taux)
except (OSError, ValueError, KeyError, TypeError, ErreurTaux) as erreur:
print(f"Erreur : {erreur}", file=sys.stderr)
return 1
print(f"{len(depenses)} dépenses enregistrées dans depenses.json")
print(f"Taux {args.base} vers {args.cible} : {taux}")
print(f"Total converti : {total:.2f} {args.cible}")
return 0
if __name__ == "__main__":
raise SystemExit(main())L’exemple suppose que `obtenir_taux(base, cible)` retourne un nombre positif ou lève `ErreurTaux`, et que la lecture CSV retourne les dictionnaires validés utilisés dans le projet. L’API est sollicitée après l’import et la sauvegarde : si elle est indisponible, le fichier JSON reste disponible, tandis que le programme affiche l’erreur et retourne un code d’échec. Les erreurs CSV déjà contextualisées continuent de remonter jusqu’à ce point d’entrée.
Arrondir uniquement le résultat présenté
Le format `:.2f` dans la dernière instruction `print` arrondit le nombre à deux décimales pour l’affichage. La valeur retournée par `calculer_total_converti` reste non arrondie, ce qui permet de la réutiliser dans d’autres calculs. Les deux devises sont indiquées dans la sortie afin que le taux et le total ne soient pas confondus.
$ python -m depenses.main releve.csv --base EUR --cible USD
2 dépenses enregistrées dans depenses.json
Taux EUR vers USD : 1.08
Total converti : 66.20 USDAvec les deux dépenses de référence, 42,80 € et 18,50 €, le total source est de 61,30 €. Au taux de 1,08, le total converti est de 66,204, affiché ici à 66,20 USD. Le taux réel dépend de la réponse de l’API au moment de l’exécution ; cette sortie illustre le calcul avec le taux indiqué.
À retenir
- Multipliez chaque montant par le taux fourni, puis additionnez les montants convertis.
- Laissez le calcul métier dans `transactions.py` et la récupération du taux dans `change.py`.
- Conservez la précision pendant le calcul et arrondissez uniquement au moment de l’affichage.
- Interceptez au point d’entrée les erreurs attendues et retournez un code d’échec sans masquer leur cause.
Dans la prochaine leçon, vous vérifierez le programme de bout en bout à partir de scénarios et de résultats attendus.