Valider les lignes importées
Objectif : Refuser les lignes CSV dont les champs requis sont absents ou mal formés et signaler leur numéro.
Un relevé peut s’ouvrir correctement tout en contenant des données inutilisables : une colonne peut manquer, une cellule être vide ou un montant ne pas être numérique. Si vous laissez passer ces lignes, l’erreur risque d’apparaître plus tard, au moment d’un calcul, sans indiquer quelle ligne du fichier est en cause.
Vous allez donc valider les données à leur arrivée et ajouter le numéro de ligne CSV au message d’erreur. La fonction conserve son rôle actuel : renvoyer une liste de dictionnaires contenant date, libelle et montant, avec le montant converti en nombre.
Le contrat attendu pour chaque ligne
Le relevé doit contenir exactement les colonnes date, libelle et montant. Chaque cellule doit avoir une valeur non vide. La date suit le format ISO AAAA-MM-JJ et le montant doit pouvoir être converti en nombre positif, conformément aux règles métier déjà établies pour les dépenses.
Dans un CSV avec une ligne d’en-tête, la première transaction se trouve à la ligne 2 du fichier. Le code ci-dessous part de ce numéro pour fournir des messages qui correspondent aux lignes que vous pouvez repérer dans un éditeur de texte ou un tableur.
Valider pendant la lecture
Remplacez le contenu de depenses/fichiers.py par le fichier complet suivant. La lecture du CSV et la conversion des montants, vues dans la leçon précédente, restent en place ; la validation intervient avant qu’une ligne soit ajoutée au résultat.
import csv
from datetime import date
COLONNES_REQUISES = {"date", "libelle", "montant"}
def lire_releve_csv(chemin):
transactions = []
with open(chemin, "r", newline="", encoding="utf-8") as fichier:
lecteur = csv.DictReader(fichier)
colonnes = lecteur.fieldnames
if colonnes is None:
raise ValueError("Le relevé CSV est vide ou ne contient pas d’en-tête.")
colonnes_manquantes = COLONNES_REQUISES - set(colonnes)
if colonnes_manquantes:
noms = ", ".join(sorted(colonnes_manquantes))
raise ValueError(f"En-tête CSV invalide : colonnes manquantes : {noms}.")
for numero_ligne, ligne in enumerate(lecteur, start=2):
if None in ligne:
raise ValueError(
f"Ligne {numero_ligne} : trop de champs dans la ligne CSV."
)
valeurs = {}
for colonne in COLONNES_REQUISES:
valeur = ligne.get(colonne)
if valeur is None or not valeur.strip():
raise ValueError(
f"Ligne {numero_ligne} : le champ '{colonne}' est absent ou vide."
)
valeurs[colonne] = valeur.strip()
try:
date.fromisoformat(valeurs["date"])
except ValueError as erreur:
raise ValueError(
f"Ligne {numero_ligne} : date invalide '{valeurs['date']}' "
f"(format attendu : AAAA-MM-JJ)."
) from erreur
try:
montant = float(valeurs["montant"])
except ValueError as erreur:
raise ValueError(
f"Ligne {numero_ligne} : montant invalide '{valeurs['montant']}'."
) from erreur
if montant <= 0:
raise ValueError(
f"Ligne {numero_ligne} : le montant doit être positif."
)
transactions.append(
{
"date": valeurs["date"],
"libelle": valeurs["libelle"],
"montant": montant,
}
)
return transactionsLe numéro de ligne est calculé avec enumerate(..., start=2) : l’en-tête occupe la ligne 1. DictReader place les champs excédentaires sous la clé None ; le contrôle correspondant les refuse. Un champ absent ou vide est également rejeté avant la conversion, ce qui évite de produire une erreur moins précise plus loin dans le traitement.
Vérifier le résultat sur un relevé
Créez un fichier releve.csv à la racine du projet avec l’en-tête attendu et deux transactions. Cette structure correspond à celle qu’utilise la fonction ; les montants sont écrits avec un point décimal pour être convertis par float.
date,libelle,montant
2025-01-15,Courses,42.80
2025-01-16,Transport,18.50Depuis la racine du projet, vous pouvez appeler la fonction directement avec une commande Python. L’import du paquet suppose que vous exécutez la commande dans le dossier qui contient depenses/.
python -c "from depenses.fichiers import lire_releve_csv; print(lire_releve_csv('releve.csv'))"[{'date': '2025-01-15', 'libelle': 'Courses', 'montant': 42.8}, {'date': '2025-01-16', 'libelle': 'Transport', 'montant': 18.5}]Pour vérifier le contexte d’erreur, remplacez par exemple 18.50 par abc, puis relancez la commande. La lecture échoue avec ValueError: Ligne 3 : montant invalide 'abc'. Une date mal formée, une cellule vide ou une ligne avec un champ supplémentaire produit de la même manière un message qui désigne la ligne fautive.
À retenir
- Vérifiez les colonnes requises avant de traiter les enregistrements.
- Contrôlez chaque valeur et ajoutez le numéro de ligne aux erreurs de données.
- Convertissez le montant seulement après avoir vérifié qu’il est présent et numérique.
- Faites remonter une erreur explicite au lieu d’ignorer une ligne invalide.
Dans la prochaine leçon, vous enregistrerez les transactions validées et les rechargerez depuis un fichier JSON.