feat: add new initialisation db mode
This commit is contained in:
79
CLAUDE.md
79
CLAUDE.md
@@ -3,6 +3,7 @@
|
||||
**Notytex** est une application web Flask moderne conçue pour la gestion complète des évaluations scolaires. Elle permet aux enseignants de créer, organiser et noter les évaluations de leurs élèves avec une interface intuitive et des fonctionnalités avancées.
|
||||
|
||||
## 🎯 **Objectif Principal**
|
||||
|
||||
Simplifier et digitaliser le processus d'évaluation scolaire, de la création des contrôles à la saisie des notes, en offrant une structure hiérarchique flexible et deux modes de notation.
|
||||
|
||||
## 🏗️ **Architecture Technique (Phase 1 - Refactorisée)**
|
||||
@@ -34,6 +35,7 @@ Grade (Note attribuée à chaque élève)
|
||||
## ⭐ **Fonctionnalités Clés**
|
||||
|
||||
### **Gestion des Évaluations**
|
||||
|
||||
- Création d'évaluations complètes avec exercices multiples
|
||||
- **Organisation par trimestre** : Chaque évaluation doit être assignée à un trimestre (1, 2 ou 3)
|
||||
- Structure hiérarchique : Assessment → Exercise → GradingElement
|
||||
@@ -43,21 +45,25 @@ Grade (Note attribuée à chaque élève)
|
||||
### **Système de Notation Unifié (Phase 2 - 2025)**
|
||||
|
||||
**2 Types de Notation Fixes :**
|
||||
|
||||
1. **`notes`** : Valeurs numériques décimales (ex: 2.5/4, 18/20, 15.5/20)
|
||||
2. **`score`** : Échelle fixe de 0 à 3 pour l'évaluation par compétences
|
||||
|
||||
**Valeurs Spéciales Configurables :**
|
||||
|
||||
- **`.`** = Pas de réponse (traité comme 0 dans les calculs)
|
||||
- **`d`** = Dispensé (ne compte pas dans la note finale)
|
||||
- **Autres valeurs** : Entièrement configurables via l'interface d'administration
|
||||
|
||||
**Configuration Centralisée :**
|
||||
|
||||
- **Signification des scores** : 0=Non acquis, 1=En cours, 2=Acquis, 3=Expert (modifiable)
|
||||
- **Couleurs associées** : Chaque niveau peut avoir sa couleur personnalisée
|
||||
- **Règles de calcul** : Logique unifiée pour tous les types de notation
|
||||
- **Interface d'administration** : Gestion complète des paramètres de notation
|
||||
|
||||
### **Interface Utilisateur & UX Moderne (Phase 2 - Décembre 2024)**
|
||||
|
||||
- **Dashboard avec statistiques en temps réel** : Cartes cliquables avec animations et gradients
|
||||
- **Pages hero modernisées** : Sections d'accueil avec gradients colorés et informations contextuelles
|
||||
- **Navigation intuitive** : Actions principales mises en avant avec boutons colorés et icônes
|
||||
@@ -72,6 +78,7 @@ Grade (Note attribuée à chaque élève)
|
||||
- **Interface cohérente** : Design system unifié avec espacements, couleurs et animations harmonieux
|
||||
|
||||
### **Analyse des Résultats Avancée**
|
||||
|
||||
- **Page de résultats complète** : Vue d'ensemble des performances de l'évaluation
|
||||
- **Statistiques descriptives** : Moyenne, médiane, minimum, maximum, écart-type
|
||||
- **Visualisation graphique** : Histogramme de distribution des notes (groupes de 1 point, de 0 au maximum)
|
||||
@@ -142,6 +149,7 @@ tail -f logs/notytex.log
|
||||
```
|
||||
|
||||
## 🧪 **Qualité du Code (Phase 1 - Renforcée)**
|
||||
|
||||
- **Tests pytest avec 100% de réussite** (100 tests ✅)
|
||||
- **Architecture découplée** : Repository Pattern + Dependency Injection
|
||||
- **Gestion d'erreurs centralisée** : Gestionnaires globaux JSON/HTML
|
||||
@@ -154,7 +162,7 @@ tail -f logs/notytex.log
|
||||
|
||||
1. **Professeur crée une évaluation** : "Contrôle Chapitre 3 - Fonctions" pour le 2ème trimestre
|
||||
2. **Définit les paramètres** : Date, trimestre (obligatoire), classe, coefficient
|
||||
3. **Ajoute des exercices** : "Exercice 1: Calculs", "Exercice 2: Graphiques"
|
||||
3. **Ajoute des exercices** : "Exercice 1: Calculs", "Exercice 2: Graphiques"
|
||||
4. **Définit le barème** : Question 1a (2 pts), Question 1b (3 pts), Compétence graphique (score 0-3)
|
||||
5. **Voit l'indicateur de progression** : "Correction 0%" en rouge sur toutes les pages
|
||||
6. **Saisit les notes** pour chaque élève sur chaque élément via clic sur l'indicateur
|
||||
@@ -162,7 +170,15 @@ tail -f logs/notytex.log
|
||||
8. **Consulte les résultats détaillés** : Accès direct à la page de résultats avec statistiques et histogramme
|
||||
9. **Analyse les performances** : Statistiques descriptives, distribution des notes et classement alphabétique
|
||||
|
||||
## Volumétrie de milieu d'année (milieu du 2e trimestre)
|
||||
|
||||
- 5 classes d'entre 25 et 35 élèves
|
||||
- Les évaluations sont constitués d'entre 10 et 20 éléments de notations
|
||||
- 4 évaluations corrigées par classes pour le premier trimestre
|
||||
- 2 évaluations non corrigée ou partiellement corrigée par classe
|
||||
|
||||
## 🎓 **Public Cible**
|
||||
|
||||
- Enseignants du secondaire (collège/lycée)
|
||||
- Établissements souhaitant digitaliser leurs évaluations
|
||||
- Contexte où coexistent notation classique et évaluation par compétences
|
||||
@@ -172,6 +188,7 @@ Ce projet présente une architecture solide, une interface soignée avec des **i
|
||||
## 🎨 **Dernières Améliorations UX**
|
||||
|
||||
### **Indicateurs de Progression Intégrés**
|
||||
|
||||
- **Calcul automatique** : Propriété `grading_progress` dans le modèle Assessment
|
||||
- **Affichage multi-pages** : Présent sur index, liste évaluations, détail évaluation
|
||||
- **Code couleur intuitif** :
|
||||
@@ -183,8 +200,9 @@ Ce projet présente une architecture solide, une interface soignée avec des **i
|
||||
- **Responsive design** : Version complète sur liste évaluations, version compacte sur index
|
||||
|
||||
### **Système de Résultats et Statistiques**
|
||||
|
||||
- **Calculs automatisés** : Méthodes `calculate_student_scores()`, `get_assessment_statistics()` dans le modèle Assessment
|
||||
- **Double logique de scoring** :
|
||||
- **Double logique de scoring** :
|
||||
- **Points** : Sommation directe des valeurs
|
||||
- **Compétences** : Formule `1/3 * score * pointMax` (score 0-3)
|
||||
- **Gestion des cas particuliers** : Les scores "." comptent comme 0 mais incluent les points maximum
|
||||
@@ -201,12 +219,14 @@ Cette évolution transforme Notytex en un outil **véritablement centré utilisa
|
||||
## 📋 **Prérequis**
|
||||
|
||||
### **Environnement de Développement**
|
||||
|
||||
- **Python 3.8+** : Version recommandée 3.11+
|
||||
- **uv** : Gestionnaire de paquets moderne Python ([installation](https://docs.astral.sh/uv/))
|
||||
- **Git** : Pour le contrôle de version
|
||||
- **IDE recommandé** : VSCode avec extensions Python, Flask, Jinja2
|
||||
|
||||
### **Connaissances Requises**
|
||||
|
||||
- **Python** : Classes, décorateurs, gestion d'erreurs
|
||||
- **Flask** : Routes, templates, blueprints, contexte d'application
|
||||
- **SQLAlchemy** : ORM, relations, requêtes
|
||||
@@ -233,6 +253,7 @@ uv run flask --app app run --debug
|
||||
## 🏗️ **Architecture Détaillée**
|
||||
|
||||
### **Structure des Fichiers**
|
||||
|
||||
```
|
||||
notytex/
|
||||
├── 📱 app.py # Point d'entrée Flask + routes principales
|
||||
@@ -257,11 +278,12 @@ notytex/
|
||||
```
|
||||
|
||||
### **Flux de Données Typique**
|
||||
|
||||
```
|
||||
1. Route Flask (routes/*.py)
|
||||
↓
|
||||
2. Validation Form (forms.py)
|
||||
↓
|
||||
↓
|
||||
3. Logique Métier (models.py)
|
||||
↓
|
||||
4. Accès Base de Données (SQLAlchemy)
|
||||
@@ -272,12 +294,15 @@ notytex/
|
||||
## 🎯 **Points d'Entrée pour Contribuer**
|
||||
|
||||
### **🌟 Débutant - Familiarisation**
|
||||
|
||||
1. **Ajouter un champ à un modèle existant**
|
||||
|
||||
- Fichier : `models.py`
|
||||
- Exemple : Ajouter un champ "commentaire" à Student
|
||||
- Impact : Migration DB + template + form
|
||||
|
||||
2. **Modifier l'apparence d'une page**
|
||||
|
||||
- Fichiers : `templates/*.html`
|
||||
- Technologie : TailwindCSS
|
||||
- Exemple : Changer les couleurs du dashboard
|
||||
@@ -288,13 +313,16 @@ notytex/
|
||||
- Exemple : Validation format email étudiant
|
||||
|
||||
### **🔥 Intermédiaire - Nouvelles Fonctionnalités**
|
||||
|
||||
1. **Créer une nouvelle page**
|
||||
|
||||
- Blueprint dans `routes/`
|
||||
- Template correspondant
|
||||
- Formulaire si nécessaire
|
||||
- Tests
|
||||
|
||||
2. **Ajouter un système d'export**
|
||||
|
||||
- Route d'export (PDF, Excel, CSV)
|
||||
- Template de génération
|
||||
- Boutons dans l'interface
|
||||
@@ -305,12 +333,15 @@ notytex/
|
||||
- Template de configuration
|
||||
|
||||
### **⚡ Avancé - Architecture**
|
||||
|
||||
1. **Optimiser les performances**
|
||||
|
||||
- Requêtes SQLAlchemy (N+1 queries)
|
||||
- Cache des calculs coûteux
|
||||
- Lazy loading intelligent
|
||||
|
||||
2. **Ajouter des API REST**
|
||||
|
||||
- Endpoints JSON
|
||||
- Authentification
|
||||
- Documentation OpenAPI
|
||||
@@ -323,6 +354,7 @@ notytex/
|
||||
## 📚 **Concepts Clés à Maîtriser**
|
||||
|
||||
### **Configuration Dynamique**
|
||||
|
||||
```python
|
||||
# Configuration stockée en base SQLite
|
||||
from app_config import config_manager
|
||||
@@ -331,12 +363,13 @@ from app_config import config_manager
|
||||
school_year = config_manager.get('context.school_year')
|
||||
competences = config_manager.get_competences_list()
|
||||
|
||||
# Écriture
|
||||
# Écriture
|
||||
config_manager.set('context.school_year', '2025-2026')
|
||||
config_manager.save()
|
||||
```
|
||||
|
||||
### **Calcul de Progression**
|
||||
|
||||
```python
|
||||
# Dans models.py - Assessment
|
||||
@property
|
||||
@@ -352,6 +385,7 @@ def grading_progress(self):
|
||||
```
|
||||
|
||||
### **Système de Notation Unifié**
|
||||
|
||||
```python
|
||||
# Type "notes" - Valeurs numériques
|
||||
grade.value = "15.5" # Points décimaux
|
||||
@@ -376,6 +410,7 @@ special_values = config_manager.get('grading.special_values')
|
||||
## 🧪 **Tests et Débogage**
|
||||
|
||||
### **Lancer les Tests**
|
||||
|
||||
```bash
|
||||
# Tous les tests
|
||||
uv run pytest
|
||||
@@ -388,6 +423,7 @@ uv run pytest tests/test_models.py -v
|
||||
```
|
||||
|
||||
### **Débogage**
|
||||
|
||||
```bash
|
||||
# Mode debug avec rechargement auto
|
||||
uv run flask --app app run --debug
|
||||
@@ -400,6 +436,7 @@ tail -f logs/school_management.log
|
||||
```
|
||||
|
||||
### **Base de Données**
|
||||
|
||||
```bash
|
||||
# Réinitialiser complètement
|
||||
rm school_management.db
|
||||
@@ -414,18 +451,21 @@ sqlite3 school_management.db
|
||||
## 🎨 **Conventions de Code**
|
||||
|
||||
### **Style Python**
|
||||
|
||||
- **PEP 8** : Formatage automatique avec black
|
||||
- **Type hints** : Recommandés pour les nouvelles fonctions
|
||||
- **Docstrings** : Format Google pour les fonctions publiques
|
||||
- **Noms explicites** : `calculate_student_scores()` plutôt que `calc()`
|
||||
|
||||
### **Templates Jinja2**
|
||||
|
||||
- **Indentation** : 4 espaces
|
||||
- **Noms de variables** : snake_case
|
||||
- **Blocs réutilisables** : Utiliser les includes et macros
|
||||
- **Classes CSS** : TailwindCSS avec composition
|
||||
|
||||
### **Base de Données**
|
||||
|
||||
- **Noms de tables** : Pluriel en anglais (`students`, `assessments`)
|
||||
- **Relations** : Toujours avec `backref` explicite
|
||||
- **Cascades** : Définir explicitement le comportement
|
||||
@@ -433,6 +473,7 @@ sqlite3 school_management.db
|
||||
## 🐛 **Problèmes Courants**
|
||||
|
||||
### **Erreur : Template Not Found**
|
||||
|
||||
```python
|
||||
# ❌ Mauvais
|
||||
return render_template('config.html')
|
||||
@@ -442,6 +483,7 @@ return render_template('config/index.html')
|
||||
```
|
||||
|
||||
### **Erreur : SQLAlchemy Session**
|
||||
|
||||
```python
|
||||
# ❌ Oublier de commit
|
||||
db.session.add(new_student)
|
||||
@@ -452,6 +494,7 @@ db.session.commit()
|
||||
```
|
||||
|
||||
### **Erreur : Import Circulaire**
|
||||
|
||||
```python
|
||||
# ❌ Import direct dans models.py
|
||||
from app import app
|
||||
@@ -465,19 +508,22 @@ def get_current_app():
|
||||
## 📖 **Ressources Utiles**
|
||||
|
||||
### **Documentation Officielle**
|
||||
|
||||
- [Flask](https://flask.palletsprojects.com/) - Framework web
|
||||
- [SQLAlchemy](https://docs.sqlalchemy.org/) - ORM Python
|
||||
- [TailwindCSS](https://tailwindcss.com/) - Framework CSS
|
||||
- [Jinja2](https://jinja.palletsprojects.com/) - Moteur de templates
|
||||
|
||||
### **Outils de Développement**
|
||||
|
||||
- [uv](https://docs.astral.sh/uv/) - Gestionnaire de paquets
|
||||
- [pytest](https://docs.pytest.org/) - Framework de tests
|
||||
- [Flask-Shell](https://flask.palletsprojects.com/en/2.3.x/shell/) - Console interactive
|
||||
|
||||
### **Extensions Recommandées VSCode**
|
||||
|
||||
- Python
|
||||
- Flask Snippets
|
||||
- Flask Snippets
|
||||
- Jinja2
|
||||
- SQLite Viewer
|
||||
- TailwindCSS IntelliSense
|
||||
@@ -506,12 +552,14 @@ La **Phase 1** de refactoring a transformé Notytex en une application **robuste
|
||||
### 🔧 **1. Configuration Externalisée Sécurisée**
|
||||
|
||||
**Avant** : Configuration en dur avec clés secrètes dans le code
|
||||
|
||||
```python
|
||||
# ❌ Ancien : Sécurité compromise
|
||||
SECRET_KEY = os.urandom(32) # Différent à chaque redémarrage
|
||||
```
|
||||
|
||||
**Après** : Configuration robuste avec validation
|
||||
|
||||
```python
|
||||
# ✅ Nouveau : Configuration sécurisée
|
||||
# config/settings.py
|
||||
@@ -525,6 +573,7 @@ class Settings:
|
||||
```
|
||||
|
||||
**🎯 Bénéfices :**
|
||||
|
||||
- **Sécurité renforcée** : Plus de données sensibles en dur
|
||||
- **Configuration flexible** : Variables d'environnement (.env)
|
||||
- **Validation au démarrage** : Échec rapide si configuration incorrecte
|
||||
@@ -533,6 +582,7 @@ class Settings:
|
||||
### 🛡️ **2. Gestion d'Erreurs Centralisée**
|
||||
|
||||
**Avant** : Gestion d'erreurs dispersée et incohérente
|
||||
|
||||
```python
|
||||
# ❌ Ancien : Gestion ad-hoc
|
||||
try:
|
||||
@@ -542,6 +592,7 @@ except Exception as e:
|
||||
```
|
||||
|
||||
**Après** : Gestionnaires d'erreurs globaux
|
||||
|
||||
```python
|
||||
# ✅ Nouveau : Gestion centralisée
|
||||
# exceptions/handlers.py
|
||||
@@ -553,6 +604,7 @@ def handle_validation_error(error):
|
||||
```
|
||||
|
||||
**🎯 Bénéfices :**
|
||||
|
||||
- **Gestion unifiée** : Toutes les erreurs traitées de manière cohérente
|
||||
- **Support JSON/HTML** : API et interface web harmonisées
|
||||
- **Logs automatiques** : Traçabilité complète des erreurs
|
||||
@@ -561,12 +613,14 @@ def handle_validation_error(error):
|
||||
### 🔍 **3. Logging Structuré JSON**
|
||||
|
||||
**Avant** : Logs textuels basiques difficiles à analyser
|
||||
|
||||
```python
|
||||
# ❌ Ancien : Logs non structurés
|
||||
app.logger.info(f'Utilisateur {user} a créé évaluation {assessment}')
|
||||
```
|
||||
|
||||
**Après** : Logs JSON avec corrélation des requêtes
|
||||
|
||||
```python
|
||||
# ✅ Nouveau : Logs structurés
|
||||
# core/logging.py
|
||||
@@ -588,6 +642,7 @@ app.logger.info(f'Utilisateur {user} a créé évaluation {assessment}')
|
||||
```
|
||||
|
||||
**🎯 Bénéfices :**
|
||||
|
||||
- **Traçabilité complète** : ID de corrélation pour suivre les requêtes
|
||||
- **Analyse facilitée** : Logs exploitables par des outils (ELK, Splunk)
|
||||
- **Contexte riche** : URL, IP, user-agent automatiquement capturés
|
||||
@@ -596,6 +651,7 @@ app.logger.info(f'Utilisateur {user} a créé évaluation {assessment}')
|
||||
### 📦 **4. Repository Pattern pour l'Accès aux Données**
|
||||
|
||||
**Avant** : Accès direct aux modèles dans les contrôleurs
|
||||
|
||||
```python
|
||||
# ❌ Ancien : Couplage fort
|
||||
def assessments_list():
|
||||
@@ -604,6 +660,7 @@ def assessments_list():
|
||||
```
|
||||
|
||||
**Après** : Couche Repository découplée
|
||||
|
||||
```python
|
||||
# ✅ Nouveau : Accès découplé
|
||||
# repositories/assessment_repository.py
|
||||
@@ -621,6 +678,7 @@ def assessments_list():
|
||||
```
|
||||
|
||||
**🎯 Bénéfices :**
|
||||
|
||||
- **Séparation des responsabilités** : Logique d'accès données isolée
|
||||
- **Réutilisabilité** : Requêtes complexes réutilisables
|
||||
- **Testabilité** : Repositories mockables indépendamment
|
||||
@@ -629,12 +687,14 @@ def assessments_list():
|
||||
## 🏆 **Résultats de la Phase 1**
|
||||
|
||||
### 📊 **Métriques de Qualité**
|
||||
|
||||
- **100 tests passent** ✅ (vs 79 avant refactoring)
|
||||
- **0 régression fonctionnelle** ✅
|
||||
- **0 régression fonctionnelle** ✅
|
||||
- **Architecture découplée** ✅
|
||||
- **Sécurité renforcée** ✅
|
||||
|
||||
### 🎯 **Prêt pour la Production**
|
||||
|
||||
- **Configuration externalisée** : Variables d'environnement
|
||||
- **Logs exploitables** : JSON structuré avec corrélation
|
||||
- **Gestion d'erreurs robuste** : Gestionnaires centralisés
|
||||
@@ -643,17 +703,20 @@ def assessments_list():
|
||||
### 🚀 **Prochaines Phases**
|
||||
|
||||
**Phase 2 - Performance & Architecture** (En cours)
|
||||
|
||||
- Services découplés avec injection de dépendances
|
||||
- Validation centralisée avec Pydantic
|
||||
- Validation centralisée avec Pydantic
|
||||
- Cache layer pour optimiser les performances
|
||||
- Pagination des listes longues
|
||||
- Métriques et monitoring avancés
|
||||
|
||||
**Phase 3 - Finalisation**
|
||||
|
||||
- Tests d'intégration complets
|
||||
- Documentation API complète
|
||||
- Pipeline CI/CD
|
||||
|
||||
---
|
||||
|
||||
**Notytex v2.0** est maintenant une application **moderne, robuste et sécurisée**, respectant les meilleures pratiques de l'industrie et prête pour un déploiement professionnel ! 🎓✨
|
||||
**Notytex v2.0** est maintenant une application **moderne, robuste et sécurisée**, respectant les meilleures pratiques de l'industrie et prête pour un déploiement professionnel ! 🎓✨
|
||||
|
||||
|
||||
95
README.md
95
README.md
@@ -73,18 +73,54 @@ uv sync
|
||||
cp .env.example .env
|
||||
# Modifier .env avec SECRET_KEY (minimum 32 caractères obligatoire)
|
||||
|
||||
# 4. Initialiser la base de données avec données de démonstration
|
||||
# 4. Initialiser la base de données (voir modes ci-dessous)
|
||||
uv run flask --app app init-db
|
||||
|
||||
# 5. Lancer l'application en mode développement
|
||||
uv run flask --app app run --debug
|
||||
```
|
||||
|
||||
### 🎯 Modes d'Initialisation de la Base de Données
|
||||
|
||||
**Notytex** propose **3 modes d'initialisation** adaptés aux différents contextes d'usage :
|
||||
|
||||
#### 🌱 **Mode Minimal** (par défaut) - Début d'année scolaire
|
||||
```bash
|
||||
uv run flask --app app init-db --mode minimal
|
||||
# ou simplement
|
||||
uv run flask --app app init-db
|
||||
```
|
||||
- **Usage** : Début d'année scolaire, base vierge
|
||||
- **Contenu** : Configuration de base uniquement
|
||||
- **Idéal pour** : Démarrer une nouvelle année avec ses vraies classes et élèves
|
||||
|
||||
#### 📚 **Mode Milieu d'Année** - Données réalistes complètes
|
||||
```bash
|
||||
uv run flask --app app init-db --mode midyear
|
||||
```
|
||||
- **Usage** : Tester l'application avec des données représentatives
|
||||
- **Contenu** :
|
||||
- **5 classes** avec 142 élèves au total (25-32 élèves par classe)
|
||||
- **30 évaluations** créées (6 par classe sur 2 trimestres)
|
||||
- **4 évaluations entièrement corrigées** par classe (Trimestre 1)
|
||||
- **2 évaluations partiellement corrigées** par classe (Trimestre 2)
|
||||
- **Notes réalistes** générées automatiquement
|
||||
- **Idéal pour** : Démonstrations, formations, tests de performance
|
||||
- **Volumétrie** : Correspond aux spécifications du CLAUDE.md (milieu de 2ème trimestre)
|
||||
|
||||
#### 🧪 **Mode Démonstration** - Données minimales
|
||||
```bash
|
||||
uv run flask --app app init-db --mode demo
|
||||
```
|
||||
- **Usage** : Tests rapides et démonstrations simples
|
||||
- **Contenu** : 2 classes, 5 élèves, 1 évaluation simple
|
||||
- **Idéal pour** : Premiers pas avec l'application
|
||||
|
||||
### 🌐 Accès à l'Application
|
||||
|
||||
- **URL de développement** : http://localhost:5000
|
||||
- **Interface** : Dashboard avec navigation intuitive
|
||||
- **Données de test** : Exemples d'évaluations et élèves préchargés
|
||||
- **Interface** : Dashboard avec navigation intuitive et indicateurs de progression
|
||||
- **Données** : Variables selon le mode d'initialisation choisi
|
||||
|
||||
## 🏗️ Architecture Technique
|
||||
|
||||
@@ -225,15 +261,19 @@ uv run pytest tests/test_repositories.py -v
|
||||
# 1. Configuration initiale
|
||||
cp .env.example .env
|
||||
# Éditer .env avec SECRET_KEY obligatoire
|
||||
uv run flask --app app init-db
|
||||
|
||||
# 2. Développement avec rechargement automatique
|
||||
# 2. Choisir le mode d'initialisation selon le contexte
|
||||
uv run flask --app app init-db --mode minimal # Début d'année
|
||||
uv run flask --app app init-db --mode midyear # Tests avec données complètes
|
||||
uv run flask --app app init-db --mode demo # Démonstration rapide
|
||||
|
||||
# 3. Développement avec rechargement automatique
|
||||
uv run flask --app app run --debug
|
||||
|
||||
# 3. Validation avant commit
|
||||
# 4. Validation avant commit
|
||||
uv run pytest
|
||||
|
||||
# 4. Analyse des logs en temps réel
|
||||
# 5. Analyse des logs en temps réel
|
||||
tail -f logs/notytex.log | jq '.' # Pour formater le JSON
|
||||
```
|
||||
|
||||
@@ -286,9 +326,46 @@ tail -f logs/notytex.log | jq '.' # Pour formater le JSON
|
||||
}
|
||||
```
|
||||
|
||||
## 🎓 Exemple d'Utilisation Complète
|
||||
## 🎓 Exemples d'Utilisation Complète
|
||||
|
||||
### 📝 Scénario : Création et Correction d'une Évaluation
|
||||
### 🌱 **Scénario Début d'Année** - Mode Minimal
|
||||
|
||||
```bash
|
||||
# Initialisation pour démarrer l'année scolaire
|
||||
uv run flask --app app init-db --mode minimal
|
||||
```
|
||||
|
||||
**Étapes typiques :**
|
||||
1. **Configuration initiale** : Année scolaire 2025-2026 automatiquement définie
|
||||
2. **Création des classes** : Ajouter manuellement 6ème A, 5ème B, etc.
|
||||
3. **Import des élèves** : Saisir ou importer les listes d'élèves réelles
|
||||
4. **Première évaluation** : Créer le premier contrôle de l'année
|
||||
|
||||
### 📚 **Scénario Milieu d'Année** - Mode Données Complètes
|
||||
|
||||
```bash
|
||||
# Initialisation avec données réalistes de milieu d'année
|
||||
uv run flask --app app init-db --mode midyear
|
||||
```
|
||||
|
||||
**Contenu automatiquement généré :**
|
||||
- **Classes** : 6ème A (28 élèves), 6ème B (25 élèves), 5ème A (30 élèves), 5ème B (27 élèves), 4ème A (32 élèves)
|
||||
- **Évaluations Trimestre 1** (100% corrigées) :
|
||||
- Contrôle Nombres entiers (coefficient 2.0)
|
||||
- Évaluation Géométrie (coefficient 2.5)
|
||||
- Contrôle Fractions (coefficient 3.0)
|
||||
- Devoir Maison Recherche (coefficient 1.5)
|
||||
- **Évaluations Trimestre 2** (partiellement corrigées) :
|
||||
- Contrôle Proportionnalité (coefficient 2.5)
|
||||
- Évaluation Statistiques (coefficient 2.0)
|
||||
|
||||
**Utilisation immédiate :**
|
||||
1. **Dashboard** : Aperçu des 142 élèves, 30 évaluations, 5 classes
|
||||
2. **Indicateurs de progression** : Visualisation des corrections en cours
|
||||
3. **Analyse des résultats** : Statistiques et graphiques sur les évaluations terminées
|
||||
4. **Saisie de notes** : Continuer la correction des évaluations partielles
|
||||
|
||||
### 📝 **Scénario Création d'Évaluation** - Workflow Standard
|
||||
|
||||
1. **🏗️ Création de l'évaluation**
|
||||
|
||||
|
||||
393
commands.py
393
commands.py
@@ -3,9 +3,11 @@ from flask.cli import with_appcontext
|
||||
from models import db, ClassGroup, Student, Assessment, Exercise, GradingElement, Domain
|
||||
|
||||
@click.command()
|
||||
@click.option('--mode', default='minimal', type=click.Choice(['minimal', 'midyear', 'demo']),
|
||||
help='Type d\'initialisation : minimal (début d\'année), midyear (milieu d\'année), demo (données de démonstration)')
|
||||
@with_appcontext
|
||||
def init_db():
|
||||
"""Initialize the database with sample data."""
|
||||
def init_db(mode):
|
||||
"""Initialize the database with different data sets."""
|
||||
db.create_all()
|
||||
|
||||
# Check if data already exists
|
||||
@@ -13,6 +15,391 @@ def init_db():
|
||||
click.echo("Database already initialized!")
|
||||
return
|
||||
|
||||
if mode == 'minimal':
|
||||
init_minimal_data()
|
||||
elif mode == 'midyear':
|
||||
init_midyear_data()
|
||||
else: # demo
|
||||
init_demo_data()
|
||||
|
||||
def init_minimal_data():
|
||||
"""Initialize database for start of school year - configuration only."""
|
||||
from app_config import config_manager
|
||||
|
||||
# Ensure basic configuration is set
|
||||
config_manager.set('context.school_year', '2025-2026')
|
||||
config_manager.set('context.school_name', 'Collège')
|
||||
config_manager.save()
|
||||
|
||||
click.echo("Database initialized for start of school year - ready to create classes and students!")
|
||||
|
||||
def init_midyear_data():
|
||||
"""Initialize database with realistic mid-year data."""
|
||||
from app_config import config_manager
|
||||
from models import Grade
|
||||
import random
|
||||
|
||||
# Set configuration
|
||||
config_manager.set('context.school_year', '2024-2025')
|
||||
config_manager.set('context.school_name', 'Collège Jean Moulin')
|
||||
config_manager.save()
|
||||
|
||||
# Create 5 classes with realistic sizes
|
||||
classes_data = [
|
||||
("6ème A", "Classe de 6ème A", 28),
|
||||
("6ème B", "Classe de 6ème B", 25),
|
||||
("5ème A", "Classe de 5ème A", 30),
|
||||
("5ème B", "Classe de 5ème B", 27),
|
||||
("4ème A", "Classe de 4ème A", 32),
|
||||
]
|
||||
|
||||
# French first and last names for realistic data
|
||||
first_names = [
|
||||
"Adrien", "Alice", "Alexandre", "Amélie", "Antoine", "Anna", "Baptiste", "Camille",
|
||||
"Charlotte", "Clément", "Chloé", "David", "Emma", "Ethan", "Elise", "Enzo", "Eva",
|
||||
"Fabien", "Fanny", "Gabriel", "Giulia", "Hugo", "Inès", "Jules", "Jade", "Kévin",
|
||||
"Léa", "Louis", "Marie", "Mathis", "Nina", "Oscar", "Paul", "Romane", "Sophie",
|
||||
"Thomas", "Valentine", "Victor", "Zoé"
|
||||
]
|
||||
|
||||
last_names = [
|
||||
"Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard", "Petit", "Durand",
|
||||
"Leroy", "Moreau", "Simon", "Laurent", "Lefebvre", "Michel", "Garcia", "David",
|
||||
"Bertrand", "Roux", "Vincent", "Fournier", "Morel", "Girard", "André", "Lefèvre",
|
||||
"Mercier", "Dupont", "Lambert", "Bonnet", "François", "Martinez", "Rousseau",
|
||||
"Garnier", "Faure", "Muller", "Henry", "Rodriguez", "Perrin"
|
||||
]
|
||||
|
||||
classes = []
|
||||
students_by_class = []
|
||||
|
||||
# Global sets to ensure uniqueness across all classes
|
||||
used_names = set()
|
||||
used_emails = set()
|
||||
|
||||
# Create classes and students
|
||||
for class_name, description, nb_students in classes_data:
|
||||
classe = ClassGroup(name=class_name, description=description, year="2024-2025")
|
||||
db.session.add(classe)
|
||||
db.session.commit()
|
||||
classes.append(classe)
|
||||
|
||||
# Create students for this class
|
||||
class_students = []
|
||||
for i in range(nb_students):
|
||||
# Generate unique name combinations and emails
|
||||
while True:
|
||||
first_name = random.choice(first_names)
|
||||
last_name = random.choice(last_names)
|
||||
name_combo = f"{first_name}_{last_name}"
|
||||
base_email = f"{first_name.lower()}.{last_name.lower()}@college.edu"
|
||||
|
||||
# Make email unique if needed
|
||||
email = base_email
|
||||
counter = 1
|
||||
while email in used_emails:
|
||||
email = f"{first_name.lower()}.{last_name.lower()}{counter}@college.edu"
|
||||
counter += 1
|
||||
|
||||
if name_combo not in used_names:
|
||||
used_names.add(name_combo)
|
||||
used_emails.add(email)
|
||||
break
|
||||
|
||||
student = Student(
|
||||
last_name=last_name,
|
||||
first_name=first_name,
|
||||
email=email,
|
||||
class_group_id=classe.id
|
||||
)
|
||||
db.session.add(student)
|
||||
class_students.append(student)
|
||||
|
||||
students_by_class.append(class_students)
|
||||
db.session.commit()
|
||||
|
||||
# Get domains for realistic grading elements
|
||||
domain_calcul = Domain.query.filter_by(name='Algèbre').first()
|
||||
domain_geometrie = Domain.query.filter_by(name='Géométrie').first()
|
||||
domain_fonctions = Domain.query.filter_by(name='Fonctions').first()
|
||||
domain_stats = Domain.query.filter_by(name='Statistiques').first()
|
||||
domain_problemes = Domain.query.filter_by(name='Problèmes').first()
|
||||
|
||||
# Create assessments for each class
|
||||
assessments_templates = [
|
||||
# Trimester 1 - completed assessments (4 per class)
|
||||
{
|
||||
"title": "Contrôle Chapitre 1 - Nombres entiers",
|
||||
"description": "Évaluation sur les opérations et la divisibilité",
|
||||
"trimester": 1,
|
||||
"coefficient": 2.0,
|
||||
"exercises": [
|
||||
{
|
||||
"title": "Ex1 - Opérations",
|
||||
"description": "Calculs et priorités opératoires",
|
||||
"elements": [
|
||||
("Additions", "Calculs simples", "Calculer", 3.0, "notes", domain_calcul),
|
||||
("Multiplications", "Tables et calculs", "Calculer", 4.0, "notes", domain_calcul),
|
||||
("Priorités", "Parenthèses et ordres", "Calculer", 3.0, "notes", domain_calcul),
|
||||
("Méthode", "Justification des étapes", "Raisonner", 2.0, "score", domain_problemes),
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Ex2 - Divisibilité",
|
||||
"description": "Critères de divisibilité et factorisation",
|
||||
"elements": [
|
||||
("Critères", "Divisibilité par 2,3,5,9", "Calculer", 4.0, "notes", domain_calcul),
|
||||
("Division euclidienne", "Quotient et reste", "Calculer", 4.0, "notes", domain_calcul),
|
||||
("Problème", "Application concrète", "Modéliser", 3.0, "score", domain_problemes),
|
||||
]
|
||||
}
|
||||
],
|
||||
"corrected": True
|
||||
},
|
||||
{
|
||||
"title": "Évaluation Chapitre 2 - Géométrie",
|
||||
"description": "Figures géométriques et mesures",
|
||||
"trimester": 1,
|
||||
"coefficient": 2.5,
|
||||
"exercises": [
|
||||
{
|
||||
"title": "Ex1 - Périmètres et aires",
|
||||
"description": "Calculs géométriques",
|
||||
"elements": [
|
||||
("Périmètre rectangle", "Formule et calcul", "Calculer", 2.0, "notes", domain_geometrie),
|
||||
("Aire triangle", "Formule et application", "Calculer", 3.0, "notes", domain_geometrie),
|
||||
("Construction", "Tracer au compas", "Représenter", 2.0, "score", domain_geometrie),
|
||||
("Rédaction", "Présentation des calculs", "Communiquer", 2.0, "score", domain_problemes),
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Ex2 - Problème de synthèse",
|
||||
"description": "Application des formules",
|
||||
"elements": [
|
||||
("Modélisation", "Schéma et données", "Modéliser", 3.0, "score", domain_problemes),
|
||||
("Calculs", "Périmètre et aire", "Calculer", 5.0, "notes", domain_geometrie),
|
||||
("Interprétation", "Sens du résultat", "Raisonner", 2.0, "score", domain_problemes),
|
||||
]
|
||||
}
|
||||
],
|
||||
"corrected": True
|
||||
},
|
||||
{
|
||||
"title": "Contrôle Chapitre 3 - Fractions",
|
||||
"description": "Opérations sur les fractions",
|
||||
"trimester": 1,
|
||||
"coefficient": 3.0,
|
||||
"exercises": [
|
||||
{
|
||||
"title": "Ex1 - Simplification",
|
||||
"description": "Fractions égales et simplification",
|
||||
"elements": [
|
||||
("Reconnaissance", "Fractions égales", "Calculer", 2.0, "notes", domain_calcul),
|
||||
("Simplification", "Réduction au plus simple", "Calculer", 4.0, "notes", domain_calcul),
|
||||
("Justification", "Expliquer la méthode", "Raisonner", 2.0, "score", domain_problemes),
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Ex2 - Opérations",
|
||||
"description": "Addition et soustraction",
|
||||
"elements": [
|
||||
("Même dénominateur", "Calculs directs", "Calculer", 3.0, "notes", domain_calcul),
|
||||
("Dénominateurs différents", "Mise au même dénominateur", "Calculer", 5.0, "notes", domain_calcul),
|
||||
("Problème", "Application pratique", "Modéliser", 3.0, "score", domain_problemes),
|
||||
]
|
||||
}
|
||||
],
|
||||
"corrected": True
|
||||
},
|
||||
{
|
||||
"title": "Devoir Maison - Recherche",
|
||||
"description": "Travail de recherche et d'approfondissement",
|
||||
"trimester": 1,
|
||||
"coefficient": 1.5,
|
||||
"exercises": [
|
||||
{
|
||||
"title": "Recherche documentaire",
|
||||
"description": "Histoire des mathématiques",
|
||||
"elements": [
|
||||
("Contenu", "Richesse des informations", "Raisonner", 3.0, "score", domain_problemes),
|
||||
("Sources", "Qualité et diversité", "Raisonner", 2.0, "score", domain_problemes),
|
||||
("Présentation", "Mise en forme", "Communiquer", 3.0, "score", domain_problemes),
|
||||
("Originalité", "Approche personnelle", "Raisonner", 2.0, "score", domain_problemes),
|
||||
]
|
||||
}
|
||||
],
|
||||
"corrected": True
|
||||
},
|
||||
|
||||
# Trimester 2 - partially corrected assessments (2 per class)
|
||||
{
|
||||
"title": "Contrôle Chapitre 4 - Proportionnalité",
|
||||
"description": "Tableaux de proportionnalité et applications",
|
||||
"trimester": 2,
|
||||
"coefficient": 2.5,
|
||||
"exercises": [
|
||||
{
|
||||
"title": "Ex1 - Reconnaissance",
|
||||
"description": "Identifier la proportionnalité",
|
||||
"elements": [
|
||||
("Tableaux", "Coefficients de proportionnalité", "Raisonner", 3.0, "notes", domain_calcul),
|
||||
("Graphiques", "Droites passant par l'origine", "Représenter", 2.0, "score", domain_geometrie),
|
||||
("Règle de trois", "Calculs simples", "Calculer", 4.0, "notes", domain_calcul),
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Ex2 - Applications",
|
||||
"description": "Problèmes de proportionnalité",
|
||||
"elements": [
|
||||
("Échelle", "Plans et cartes", "Modéliser", 4.0, "notes", domain_geometrie),
|
||||
("Vitesse", "Distance-temps", "Modéliser", 3.0, "notes", domain_calcul),
|
||||
("Pourcentages", "Calculs et applications", "Calculer", 4.0, "notes", domain_calcul),
|
||||
("Méthode", "Présentation claire", "Communiquer", 2.0, "score", domain_problemes),
|
||||
]
|
||||
}
|
||||
],
|
||||
"corrected": False # Partiellement corrigé
|
||||
},
|
||||
{
|
||||
"title": "Évaluation Chapitre 5 - Statistiques",
|
||||
"description": "Moyenne, médiane, graphiques",
|
||||
"trimester": 2,
|
||||
"coefficient": 2.0,
|
||||
"exercises": [
|
||||
{
|
||||
"title": "Ex1 - Calculs statistiques",
|
||||
"description": "Indicateurs de position",
|
||||
"elements": [
|
||||
("Moyenne", "Calcul et interprétation", "Calculer", 3.0, "notes", domain_stats),
|
||||
("Médiane", "Valeur centrale", "Calculer", 3.0, "notes", domain_stats),
|
||||
("Étendue", "Maximum - minimum", "Calculer", 2.0, "notes", domain_stats),
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Ex2 - Représentations",
|
||||
"description": "Graphiques statistiques",
|
||||
"elements": [
|
||||
("Diagramme", "Construction soignée", "Représenter", 3.0, "score", domain_stats),
|
||||
("Lecture", "Extraire des informations", "Raisonner", 2.0, "score", domain_stats),
|
||||
("Interprétation", "Analyser les résultats", "Raisonner", 3.0, "score", domain_stats),
|
||||
("Communication", "Explication claire", "Communiquer", 2.0, "score", domain_problemes),
|
||||
]
|
||||
}
|
||||
],
|
||||
"corrected": False # Non corrigé
|
||||
}
|
||||
]
|
||||
|
||||
# Create assessments for each class
|
||||
for i, classe in enumerate(classes):
|
||||
class_students = students_by_class[i]
|
||||
|
||||
for assessment_template in assessments_templates:
|
||||
# Create assessment
|
||||
assessment = Assessment(
|
||||
title=assessment_template["title"],
|
||||
description=assessment_template["description"],
|
||||
trimester=assessment_template["trimester"],
|
||||
class_group_id=classe.id,
|
||||
coefficient=assessment_template["coefficient"]
|
||||
)
|
||||
db.session.add(assessment)
|
||||
db.session.commit()
|
||||
|
||||
# Create exercises and grading elements
|
||||
for ex_template in assessment_template["exercises"]:
|
||||
exercise = Exercise(
|
||||
assessment_id=assessment.id,
|
||||
title=ex_template["title"],
|
||||
description=ex_template["description"],
|
||||
order=assessment_template["exercises"].index(ex_template) + 1
|
||||
)
|
||||
db.session.add(exercise)
|
||||
db.session.commit()
|
||||
|
||||
# Create grading elements
|
||||
grading_elements = []
|
||||
for element_data in ex_template["elements"]:
|
||||
label, desc, skill, max_points, grading_type, domain = element_data
|
||||
|
||||
element = GradingElement(
|
||||
exercise_id=exercise.id,
|
||||
label=label,
|
||||
description=desc,
|
||||
skill=skill,
|
||||
max_points=max_points,
|
||||
grading_type=grading_type,
|
||||
domain_id=domain.id if domain else None
|
||||
)
|
||||
db.session.add(element)
|
||||
grading_elements.append(element)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Add grades if assessment should be corrected
|
||||
if assessment_template["corrected"]:
|
||||
# Generate realistic grades for all students
|
||||
for student in class_students:
|
||||
for element in grading_elements:
|
||||
# Generate realistic grade based on element type
|
||||
if element.grading_type == "notes":
|
||||
# Points: usually between 60-95% of max_points
|
||||
base_ratio = random.uniform(0.6, 0.95)
|
||||
grade_value = round(base_ratio * element.max_points, 1)
|
||||
# Some students get perfect scores (10% chance)
|
||||
if random.random() < 0.1:
|
||||
grade_value = element.max_points
|
||||
# Some students struggle (15% chance)
|
||||
elif random.random() < 0.15:
|
||||
grade_value = round(random.uniform(0.2, 0.6) * element.max_points, 1)
|
||||
else: # score type (0-3)
|
||||
# Competence scores: weighted towards 1 and 2
|
||||
score_weights = [0.1, 0.35, 0.45, 0.1] # Probabilities for 0,1,2,3
|
||||
grade_value = random.choices([0, 1, 2, 3], weights=score_weights)[0]
|
||||
|
||||
# Occasionally add special values (5% chance)
|
||||
if random.random() < 0.05:
|
||||
grade_value = random.choice([".", "d"])
|
||||
|
||||
grade = Grade(
|
||||
student_id=student.id,
|
||||
grading_element_id=element.id,
|
||||
value=str(grade_value)
|
||||
)
|
||||
db.session.add(grade)
|
||||
|
||||
elif assessment_template.get("corrected") == False and random.random() < 0.4:
|
||||
# Partially grade some assessments (40% of non-corrected ones get partial grades)
|
||||
students_to_grade = random.sample(class_students, len(class_students) // 2)
|
||||
|
||||
for student in students_to_grade:
|
||||
for element in grading_elements:
|
||||
if random.random() < 0.7: # Grade 70% of elements for selected students
|
||||
if element.grading_type == "notes":
|
||||
base_ratio = random.uniform(0.6, 0.9)
|
||||
grade_value = round(base_ratio * element.max_points, 1)
|
||||
else:
|
||||
grade_value = random.choices([0, 1, 2, 3], weights=[0.1, 0.3, 0.5, 0.1])[0]
|
||||
|
||||
grade = Grade(
|
||||
student_id=student.id,
|
||||
grading_element_id=element.id,
|
||||
value=str(grade_value)
|
||||
)
|
||||
db.session.add(grade)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
total_students = sum(nb for _, _, nb in classes_data)
|
||||
total_assessments = len(classes) * len(assessments_templates)
|
||||
click.echo(f"Database initialized for mid-year scenario:")
|
||||
click.echo(f" - {len(classes)} classes with {total_students} students total")
|
||||
click.echo(f" - {total_assessments} assessments created")
|
||||
click.echo(f" - 4 fully corrected assessments per class (Trimester 1)")
|
||||
click.echo(f" - 2 partially corrected assessments per class (Trimester 2)")
|
||||
|
||||
def init_demo_data():
|
||||
"""Initialize database with simple demo data (original behavior)."""
|
||||
# Create sample class groups
|
||||
classe_6a = ClassGroup(name="6ème A", description="Classe de 6ème A", year="2024-2025")
|
||||
classe_5b = ClassGroup(name="5ème B", description="Classe de 5ème B", year="2024-2025")
|
||||
@@ -86,7 +473,7 @@ def init_db():
|
||||
db.session.add(element)
|
||||
|
||||
db.session.commit()
|
||||
click.echo("Database initialized with sample data!")
|
||||
click.echo("Database initialized with demo data!")
|
||||
|
||||
@click.command()
|
||||
@with_appcontext
|
||||
|
||||
Reference in New Issue
Block a user