Objectif du projet : Créer une application complète de gestion de tâches (To-Do List) qui intègre les concepts essentiels de Python : programmation orientée objet, gestion de fichiers, exceptions, tests, et interface en ligne de commande.
1. Aperçu du projet
Nous allons construire une application de gestion de tâches avec les fonctionnalités suivantes :
- Ajouter, modifier et supprimer des tâches
- Marquer une tâche comme complétée
- Persistance des données (sauvegarde dans un fichier JSON)
- Affichage des tâches (toutes, complétées, en cours)
- Tests unitaires pour valider le fonctionnement
- Interface en ligne de commande intuitive
Structure du projet :
todo_project/
│
├── todo.py # Modèle de données et classes principales
├── storage.py # Gestion de la persistance (JSON)
├── cli.py # Interface utilisateur
├── tests.py # Tests unitaires
└── data/
└── tasks.json # Fichier de données
2. Modèle de données (todo.py)
from datetime import datetime
import uuid
class Tache:
"""Classe représentant une tâche individuelle"""
def __init__(self, titre, description=""):
self.id = str(uuid.uuid4())[:8]
self.titre = titre
self.description = description
self.complete = False
self.date_creation = datetime.now().isoformat()
self.date_completion = None
def marquer_completee(self):
"""Marque la tâche comme complétée"""
if not self.complete:
self.complete = True
self.date_completion = datetime.now().isoformat()
def to_dict(self):
"""Convertit la tâche en dictionnaire pour JSON"""
return {
"id": self.id,
"titre": self.titre,
"description": self.description,
"complete": self.complete,
"date_creation": self.date_creation,
"date_completion": self.date_completion
}
@classmethod
def from_dict(cls, data):
"""Crée une tâche à partir d'un dictionnaire"""
tache = cls(data["titre"], data["description"])
tache.id = data["id"]
tache.complete = data["complete"]
tache.date_creation = data["date_creation"]
tache.date_completion = data["date_completion"]
return tache
def __str__(self):
statut = "✓" if self.complete else "○"
return f"[{statut}] {self.id} - {self.titre}"
class GestionnaireTaches:
"""Gère la collection de tâches"""
def __init__(self):
self.taches = []
def ajouter(self, titre, description=""):
"""Ajoute une nouvelle tâche"""
tache = Tache(titre, description)
self.taches.append(tache)
return tache
def supprimer(self, tache_id):
"""Supprime une tâche par son ID"""
tache = self.trouver_par_id(tache_id)
if tache:
self.taches.remove(tache)
return True
return False
def marquer_completee(self, tache_id):
"""Marque une tâche comme complétée"""
tache = self.trouver_par_id(tache_id)
if tache:
tache.marquer_completee()
return True
return False
def trouver_par_id(self, tache_id):
"""Trouve une tâche par son ID"""
for tache in self.taches:
if tache.id == tache_id:
return tache
return None
def lister_toutes(self):
"""Retourne toutes les tâches"""
return self.taches
def lister_completes(self):
"""Retourne les tâches complétées"""
return [t for t in self.taches if t.complete]
def lister_en_cours(self):
"""Retourne les tâches non complétées"""
return [t for t in self.taches if not t.complete]
3. Persistance des données (storage.py)
import json
import os
from todo import Tache, GestionnaireTaches
class StockageJSON:
"""Gère la persistance des tâches dans un fichier JSON"""
def __init__(self, chemin_fichier="data/tasks.json"):
self.chemin_fichier = chemin_fichier
self._assurer_dossier()
def _assurer_dossier(self):
"""Crée le dossier data s'il n'existe pas"""
dossier = os.path.dirname(self.chemin_fichier)
if dossier and not os.path.exists(dossier):
os.makedirs(dossier)
def sauvegarder(self, gestionnaire):
"""Sauvegarde toutes les tâches dans le fichier JSON"""
try:
donnees = [tache.to_dict() for tache in gestionnaire.lister_toutes()]
with open(self.chemin_fichier, 'w', encoding='utf-8') as f:
json.dump(donnees, f, indent=2, ensure_ascii=False)
return True
except Exception as e:
print(f"Erreur lors de la sauvegarde: {e}")
return False
def charger(self):
"""Charge les tâches depuis le fichier JSON"""
gestionnaire = GestionnaireTaches()
if not os.path.exists(self.chemin_fichier):
return gestionnaire
try:
with open(self.chemin_fichier, 'r', encoding='utf-8') as f:
donnees = json.load(f)
for item in donnees:
tache = Tache.from_dict(item)
gestionnaire.taches.append(tache)
except Exception as e:
print(f"Erreur lors du chargement: {e}")
return gestionnaire
4. Interface utilisateur (cli.py)
import sys
from todo import GestionnaireTaches
from storage import StockageJSON
def afficher_menu():
print("\n" + "="*50)
print("📋 GESTIONNAIRE DE TÂCHES")
print("="*50)
print("1. Ajouter une tâche")
print("2. Lister toutes les tâches")
print("3. Lister les tâches en cours")
print("4. Lister les tâches complétées")
print("5. Marquer une tâche comme complétée")
print("6. Supprimer une tâche")
print("7. Quitter")
print("-"*50)
def afficher_taches(taches, titre="Tâches"):
print(f"\n{titre}:")
if not taches:
print(" Aucune tâche.")
return
for tache in taches:
statut = "✓" if tache.complete else "○"
print(f" [{statut}] {tache.id} : {tache.titre}")
if tache.description:
print(f" 📝 {tache.description}")
def main():
"""Fonction principale de l'application"""
stockage = StockageJSON()
gestionnaire = stockage.charger()
print("Bienvenue dans votre gestionnaire de tâches!")
while True:
afficher_menu()
choix = input("Votre choix: ").strip()
if choix == "1":
titre = input("Titre de la tâche: ").strip()
if titre:
description = input("Description (optionnelle): ").strip()
tache = gestionnaire.ajouter(titre, description)
stockage.sauvegarder(gestionnaire)
print(f"✅ Tâche ajoutée (ID: {tache.id})")
else:
print("❌ Le titre ne peut pas être vide.")
elif choix == "2":
afficher_taches(gestionnaire.lister_toutes(), "Toutes les tâches")
elif choix == "3":
afficher_taches(gestionnaire.lister_en_cours(), "Tâches en cours")
elif choix == "4":
afficher_taches(gestionnaire.lister_completes(), "Tâches complétées")
elif choix == "5":
tache_id = input("ID de la tâche à compléter: ").strip()
if gestionnaire.marquer_completee(tache_id):
stockage.sauvegarder(gestionnaire)
print("✅ Tâche marquée comme complétée!")
else:
print("❌ Tâche non trouvée.")
elif choix == "6":
tache_id = input("ID de la tâche à supprimer: ").strip()
if gestionnaire.supprimer(tache_id):
stockage.sauvegarder(gestionnaire)
print("🗑️ Tâche supprimée!")
else:
print("❌ Tâche non trouvée.")
elif choix == "7":
print("👋 Au revoir!")
stockage.sauvegarder(gestionnaire)
break
else:
print("❌ Choix invalide. Veuillez réessayer.")
if __name__ == "__main__":
main()
5. Tests unitaires (tests.py)
import unittest
import tempfile
import os
from todo import Tache, GestionnaireTaches
from storage import StockageJSON
class TestTache(unittest.TestCase):
def test_creation_tache(self):
tache = Tache("Test", "Description test")
self.assertEqual(tache.titre, "Test")
self.assertEqual(tache.description, "Description test")
self.assertFalse(tache.complete)
self.assertIsNotNone(tache.id)
def test_marquer_completee(self):
tache = Tache("Test")
tache.marquer_completee()
self.assertTrue(tache.complete)
self.assertIsNotNone(tache.date_completion)
def test_conversion_dict(self):
tache = Tache("Test", "Desc")
tache.marquer_completee()
d = tache.to_dict()
tache2 = Tache.from_dict(d)
self.assertEqual(tache.id, tache2.id)
self.assertEqual(tache.titre, tache2.titre)
self.assertEqual(tache.complete, tache2.complete)
class TestGestionnaireTaches(unittest.TestCase):
def setUp(self):
self.gestionnaire = GestionnaireTaches()
self.tache1 = self.gestionnaire.ajouter("Tâche 1")
self.tache2 = self.gestionnaire.ajouter("Tâche 2")
def test_ajouter(self):
self.assertEqual(len(self.gestionnaire.lister_toutes()), 2)
def test_supprimer(self):
resultat = self.gestionnaire.supprimer(self.tache1.id)
self.assertTrue(resultat)
self.assertEqual(len(self.gestionnaire.lister_toutes()), 1)
def test_marquer_completee(self):
resultat = self.gestionnaire.marquer_completee(self.tache1.id)
self.assertTrue(resultat)
self.assertTrue(self.tache1.complete)
self.assertEqual(len(self.gestionnaire.lister_en_cours()), 1)
self.assertEqual(len(self.gestionnaire.lister_completes()), 1)
class TestStockage(unittest.TestCase):
def test_sauvegarde_et_chargement(self):
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as tmp:
tmp_path = tmp.name
gestionnaire = GestionnaireTaches()
gestionnaire.ajouter("Test sauvegarde")
stockage = StockageJSON(tmp_path)
stockage.sauvegarder(gestionnaire)
nouveau_gestionnaire = stockage.charger()
self.assertEqual(
len(gestionnaire.lister_toutes()),
len(nouveau_gestionnaire.lister_toutes())
)
os.remove(tmp_path)
if __name__ == '__main__':
unittest.main()
Pour aller plus loin :
- ➕ Ajoutez des catégories ou des étiquettes aux tâches
- 📅 Ajoutez des dates d'échéance et des rappels
- 🎨 Créez une interface graphique avec Tkinter ou PyQt
- ☁️ Synchronisez les tâches avec un serveur (API REST)
- 📧 Envoyez des rappels par email
- 🔍 Ajoutez une fonction de recherche
Félicitations ! Vous avez complété les 10 leçons Python. Vous maîtrisez maintenant les bases essentielles du langage. Continuez à pratiquer et à explorer de nouveaux projets pour progresser !