354 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			354 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # 📏 Configuration des Échelles - Notytex
 | |
| 
 | |
| ## Vue d'ensemble
 | |
| 
 | |
| Notytex utilise un système de notation hybride qui distingue trois types de valeurs d'évaluation :
 | |
| 
 | |
| 1. **Notes** : Valeurs numériques décimales (ex: 2.5/4, 18/20, 15.5/20)
 | |
| 2. **Scores** : Échelle fixe de 0 à 3 pour l'évaluation par compétences
 | |
| 3. **Valeurs spéciales** : Valeurs configurables comme "." (non évalué), "d" (dispensé), etc.
 | |
| 
 | |
| ## Architecture Technique
 | |
| 
 | |
| ### Modèle de Données
 | |
| 
 | |
| ```python
 | |
| # Modèle CompetenceScaleValue
 | |
| class CompetenceScaleValue(db.Model):
 | |
|     value: str              # "0", "1", "2", "3", ".", "d", etc.
 | |
|     label: str              # "Non acquis", "En cours d'acquisition", etc.
 | |
|     color: str              # "#dc2626", "#059669", etc.
 | |
|     included_in_total: bool # True/False pour inclusion dans calculs
 | |
| ```
 | |
| 
 | |
| ### Configuration Centralisée
 | |
| 
 | |
| La configuration est gérée par `app_config.py` avec stockage en SQLite :
 | |
| 
 | |
| ```python
 | |
| # Configuration des dégradés de couleurs pour les notes
 | |
| 'grading.notes_gradient.min_color': '#dc2626'  # Rouge pour note 0
 | |
| 'grading.notes_gradient.max_color': '#059669'  # Vert pour note max
 | |
| 'grading.notes_gradient.enabled': True
 | |
| ```
 | |
| 
 | |
| ## Types de Notation
 | |
| 
 | |
| ### 1. Notes (Numerical Grading)
 | |
| 
 | |
| **Caractéristiques :**
 | |
| - Valeurs numériques décimales : 2.5, 18, 15.5, etc.
 | |
| - Barème variable selon l'exercice : /4, /20, /10, etc.
 | |
| - Calcul automatique des pourcentages et moyennes
 | |
| - **Nouveau** : Système de dégradé de couleurs automatique
 | |
| 
 | |
| **Dégradé de Couleurs (2025) :**
 | |
| - Configuration via interface `/config/scale`
 | |
| - Couleur minimum pour note 0 (par défaut : rouge #dc2626)
 | |
| - Couleur maximum pour note maximale (par défaut : vert #059669)
 | |
| - Interpolation HSL pour des transitions naturelles (évite le gris)
 | |
| - Calcul automatique des couleurs intermédiaires selon ratio note/maximum
 | |
| 
 | |
| ```javascript
 | |
| // Exemple de calcul de couleur
 | |
| function calculateNoteColor(note, maxPoints, minColor, maxColor) {
 | |
|     const factor = note / maxPoints;
 | |
|     return interpolateColorHSL(minColor, maxColor, factor);
 | |
| }
 | |
| ```
 | |
| 
 | |
| **Interface Utilisateur :**
 | |
| - Sélecteurs de couleurs min/max sur une ligne
 | |
| - Barre de dégradé visuelle avec interpolation HSL
 | |
| - Exemples de notes positionnés sur le dégradé (5/20, 10/20, 15/20)
 | |
| - Sauvegarde intégrée au formulaire principal
 | |
| 
 | |
| ### 2. Scores (Competence Grading)
 | |
| 
 | |
| **Caractéristiques :**
 | |
| - Échelle fixe et non configurable : **0, 1, 2, 3**
 | |
| - Signification par défaut :
 | |
|   - **0** : Non acquis (rouge #dc2626)
 | |
|   - **1** : En cours d'acquisition (orange #ea580c)
 | |
|   - **2** : Acquis (vert #059669)
 | |
|   - **3** : Expert (bleu #2563eb)
 | |
| - Chaque niveau peut être personnalisé (libellé, couleur, inclusion)
 | |
| 
 | |
| **Configuration :**
 | |
| - Labels modifiables via interface
 | |
| - Couleurs personnalisables avec sélecteurs
 | |
| - Option d'inclusion/exclusion du calcul global
 | |
| - Valeurs 0-3 dans la section "Échelle numérique"
 | |
| 
 | |
| ### 3. Valeurs Spéciales
 | |
| 
 | |
| **Valeurs Prédéfinies :**
 | |
| - **"."** : Non évalué (par défaut gris #6b7280)
 | |
|   - Compte comme 0 dans les notes mais inclus dans le total possible
 | |
|   - Généralement incluse pour maintenir la cohérence des calculs
 | |
| - **"d"** : Dispensé (configurable)
 | |
|   - Exclu des calculs par défaut
 | |
| 
 | |
| **Valeurs Personnalisées :**
 | |
| - Ajout via interface : "NA", "ABS", "X", etc.
 | |
| - Configuration complète : valeur, libellé, couleur, inclusion
 | |
| - Suppression possible (sauf valeurs de base)
 | |
| 
 | |
| ## Interface de Configuration
 | |
| 
 | |
| ### Page `/config/scale` - "Échelle de réussite"
 | |
| 
 | |
| **Structure :**
 | |
| 1. **Dégradé de couleurs pour les notes**
 | |
|    - Sélecteur couleur minimum (gauche)
 | |
|    - Barre de dégradé visuelle (centre, pleine largeur)
 | |
|    - Sélecteur couleur maximum (droite)
 | |
|    - Exemples positionnés sur le dégradé
 | |
| 
 | |
| 2. **Échelle numérique (0 à 3)**
 | |
|    - Configuration des 4 niveaux fixes
 | |
|    - Libellé, couleur et inclusion pour chaque niveau
 | |
|    - Interface en grille responsive
 | |
| 
 | |
| 3. **Valeurs spéciales**
 | |
|    - Liste des valeurs non-numériques
 | |
|    - Ajout/modification/suppression
 | |
|    - Protection de la valeur "." (non supprimable)
 | |
| 
 | |
| **Sauvegarde Unifiée :**
 | |
| - Bouton unique : "Enregistrer les paramètres"
 | |
| - Sauvegarde simultanée : échelle + dégradé + valeurs spéciales
 | |
| - Messages de succès différenciés selon les modifications
 | |
| 
 | |
| ## Utilisation dans le Code
 | |
| 
 | |
| ### Application dans la Page de Notation
 | |
| 
 | |
| Le dégradé HSL est **automatiquement appliqué dans la page de notation** (`/assessments/<id>/grading`) pour tous les éléments de type `"notes"`.
 | |
| 
 | |
| #### Transmission de la Configuration
 | |
| 
 | |
| ```python
 | |
| # routes/grading.py - Route assessment_grading()
 | |
| notes_gradient = {
 | |
|     'min_color': config_manager.get('grading.notes_gradient.min_color', '#dc2626'),
 | |
|     'max_color': config_manager.get('grading.notes_gradient.max_color', '#059669'),
 | |
|     'enabled': config_manager.get('grading.notes_gradient.enabled', False)
 | |
| }
 | |
| 
 | |
| return render_template('assessment_grading.html', 
 | |
|                      notes_gradient=notes_gradient,  # ← Nouvelle transmission
 | |
|                      # ... autres paramètres
 | |
|                      )
 | |
| ```
 | |
| 
 | |
| #### Calcul Dynamique des Couleurs
 | |
| 
 | |
| ```javascript
 | |
| // Fonction globale pour calculer la couleur d'une note
 | |
| window.getNotesGradientColor = function(note, maxPoints) {
 | |
|     if (!GRADING_CONFIG.notes_gradient.enabled) return '#6b7280';
 | |
|     
 | |
|     const factor = Math.min(1, Math.max(0, note / maxPoints));
 | |
|     return interpolateColorHSL(
 | |
|         GRADING_CONFIG.notes_gradient.min_color,
 | |
|         GRADING_CONFIG.notes_gradient.max_color,
 | |
|         factor
 | |
|     );
 | |
| };
 | |
| ```
 | |
| 
 | |
| #### Application Automatique
 | |
| 
 | |
| ```javascript
 | |
| // ColorManager.applyColorToInput() - Logique de colorisation
 | |
| if (type === 'notes') {
 | |
|     // Valeurs spéciales (., d, etc.) → couleurs échelle
 | |
|     if (GRADING_CONFIG.scale_values[value] && isNaN(value)) {
 | |
|         // Couleur spéciale avec style italique
 | |
|     }
 | |
|     
 | |
|     // Notes numériques (2, 2.0, 15.5, etc.) → dégradé HSL
 | |
|     const numValue = parseFloat(value);
 | |
|     if (!isNaN(numValue)) {
 | |
|         const noteColor = window.getNotesGradientColor(numValue, maxPoints);
 | |
|         input.style.color = '#374151';              // Texte gris doux
 | |
|         input.style.backgroundColor = hexToRgba(noteColor, 0.25);  // Fond coloré
 | |
|         input.style.borderColor = hexToRgba(noteColor, 0.5);       // Bordure accentuée
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Accès aux Valeurs d'Échelle
 | |
| 
 | |
| ```python
 | |
| # Repository pattern pour l'accès aux données
 | |
| competence_scale = config_manager.get_competence_scale_values()
 | |
| 
 | |
| # Structure retournée
 | |
| {
 | |
|     '0': {'label': 'Non acquis', 'color': '#dc2626', 'included_in_total': True},
 | |
|     '1': {'label': 'En cours', 'color': '#ea580c', 'included_in_total': True},
 | |
|     '2': {'label': 'Acquis', 'color': '#059669', 'included_in_total': True},
 | |
|     '3': {'label': 'Expert', 'color': '#2563eb', 'included_in_total': True},
 | |
|     '.': {'label': 'Non évalué', 'color': '#6b7280', 'included_in_total': True}
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Interpolation des Couleurs
 | |
| 
 | |
| ### Problématique RGB vs HSL
 | |
| 
 | |
| **RGB (Ancien système) :**
 | |
| - Interpolation linéaire entre composantes R, G, B
 | |
| - Problème : transitions via des couleurs "sales" (gris/marron)
 | |
| - Exemple : Rouge → Vert passe par un gris terne
 | |
| 
 | |
| **HSL (Nouveau système) :**
 | |
| - Interpolation dans l'espace teinte-saturation-luminosité
 | |
| - Transitions naturelles via le cercle chromatique
 | |
| - Gestion du "chemin le plus court" pour les teintes
 | |
| - Résultat : dégradés vibrants et naturels
 | |
| 
 | |
| ### Implementation JavaScript
 | |
| 
 | |
| ```javascript
 | |
| function interpolateColorHSL(color1, color2, factor) {
 | |
|     const rgb1 = hexToRgb(color1);
 | |
|     const rgb2 = hexToRgb(color2);
 | |
|     
 | |
|     const hsl1 = rgbToHsl(rgb1.r, rgb1.g, rgb1.b);
 | |
|     const hsl2 = rgbToHsl(rgb2.r, rgb2.g, rgb2.b);
 | |
|     
 | |
|     // Gérer transition teinte (chemin le plus court)
 | |
|     let deltaH = hsl2.h - hsl1.h;
 | |
|     if (deltaH > 180) hsl2.h -= 360;
 | |
|     else if (deltaH < -180) hsl2.h += 360;
 | |
|     
 | |
|     // Interpolation HSL
 | |
|     const h = hsl1.h + (hsl2.h - hsl1.h) * factor;
 | |
|     const s = hsl1.s + (hsl2.s - hsl1.s) * factor;
 | |
|     const l = hsl1.l + (hsl2.l - hsl1.l) * factor;
 | |
|     
 | |
|     const rgb = hslToRgb(h, s, l);
 | |
|     return rgbToHex(rgb.r, rgb.g, rgb.b);
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Routes et Endpoints
 | |
| 
 | |
| ### Configuration des Échelles
 | |
| 
 | |
| ```python
 | |
| # Routes principales
 | |
| GET  /config/scale              # Interface de configuration
 | |
| POST /config/scale/update       # Sauvegarde unifiée (échelle + dégradé)
 | |
| POST /config/scale/add          # Ajouter valeur spéciale
 | |
| POST /config/scale/delete/<val> # Supprimer valeur spéciale
 | |
| POST /config/scale/reset        # Réinitialisation par défaut
 | |
| 
 | |
| # Route dépréciée (fusionnée dans update_scale)
 | |
| POST /config/scale/notes-gradient  # Ancienne sauvegarde séparée
 | |
| ```
 | |
| 
 | |
| ### Validation des Données
 | |
| 
 | |
| ```python
 | |
| # Validation couleurs hexadécimales
 | |
| if not re.match(r'^#[0-9a-fA-F]{6}$', color):
 | |
|     flash('Format de couleur invalide', 'error')
 | |
| 
 | |
| # Protection valeurs de base
 | |
| base_values = ['0', '1', '2', '3', '.']
 | |
| if value in base_values:
 | |
|     flash('Valeur de base non supprimable', 'error')
 | |
| ```
 | |
| 
 | |
| ## Evolution et Améliorations
 | |
| 
 | |
| ### Phase Actuelle (2025)
 | |
| 
 | |
| ✅ **Réalisé :**
 | |
| - Système de dégradé HSL pour les notes
 | |
| - Interface unifiée de configuration
 | |
| - Sauvegarde intégrée échelle + dégradé
 | |
| - Exemples visuels positionnés sur le dégradé
 | |
| - Validation et protection des données
 | |
| 
 | |
| ### Évolutions Possibles
 | |
| 
 | |
| 🔮 **Futures améliorations :**
 | |
| - Export/import de configurations d'échelles
 | |
| - Présets de couleurs (thèmes prédéfinis)
 | |
| - Aperçu temps réel sur vraies données
 | |
| - Historique des modifications
 | |
| - Configuration par classe/matière
 | |
| - API REST pour configuration programmatique
 | |
| 
 | |
| ## Cas d'Usage Typiques
 | |
| 
 | |
| ### Enseignant Mathématiques
 | |
| ```
 | |
| Échelle 0-3 : Non acquis → En cours → Acquis → Expert
 | |
| Dégradé notes : Rouge foncé → Vert clair (sur /20)
 | |
| Valeurs spéciales : "." pour absents, "d" pour dispensés sport
 | |
| ```
 | |
| 
 | |
| ### Enseignant Langues  
 | |
| ```
 | |
| Échelle 0-3 : A découvrir → En apprentissage → Maîtrisé → Excellence
 | |
| Dégradé notes : Orange → Bleu (évaluations /10)
 | |
| Valeurs spéciales : "NA" pour non applicable, "O" pour oral seulement
 | |
| ```
 | |
| 
 | |
| ## Indicateurs Visuels d'Erreur
 | |
| 
 | |
| ### Validation des Saisies
 | |
| 
 | |
| Le système détecte automatiquement les **valeurs interdites** et applique un indicateur visuel très visible :
 | |
| 
 | |
| ```javascript
 | |
| // Valeurs interdites → Indicateur rouge vif
 | |
| if (!isValid) {
 | |
|     input.style.color = '#ffffff';           // Texte blanc
 | |
|     input.style.backgroundColor = '#dc2626'; // Fond rouge vif
 | |
|     input.style.borderColor = '#dc2626';     // Bordure rouge
 | |
|     input.style.borderWidth = '2px';         // Bordure épaisse
 | |
|     input.style.boxShadow = '0 0 0 3px rgba(220, 38, 38, 0.3)'; // Ombre rouge
 | |
|     input.style.fontWeight = 'bold';         // Texte en gras
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Exemples d'Erreurs Détectées
 | |
| 
 | |
| **Pour les scores (type="score")** :
 | |
| - Valeurs autres que 0, 1, 2, 3 ou valeurs spéciales configurées
 | |
| - Exemples : `"5"`, `"1.5"`, `"abc"`, `"-1"`
 | |
| 
 | |
| **Pour les notes (type="notes")** :
 | |
| - Valeurs négatives : `"-5"`
 | |
| - Valeurs supérieures au maximum : `"25"` (si max=20)
 | |
| - Format invalide : `"abc"`, `"2.5.3"`, `"10,5,2"`
 | |
| 
 | |
| ### Reset Automatique
 | |
| Dès qu'une valeur valide est saisie, tous les styles d'erreur sont automatiquement effacés et remplacés par la colorisation normale.
 | |
| 
 | |
| ## Impact UX
 | |
| 
 | |
| ### Bénéfices Utilisateur
 | |
| - **Cohérence visuelle** : Couleurs automatiques selon performance
 | |
| - **Feedback immédiat** : Erreurs visibles instantanément + dégradé temps réel
 | |
| - **Distinction claire** : Notes vs Scores vs Valeurs spéciales
 | |
| - **Simplicité** : Configuration centralisée, application automatique
 | |
| - **Flexibilité** : Adaptation aux pratiques pédagogiques
 | |
| 
 | |
| ### Performance
 | |
| - **Client** : Calculs JavaScript optimisés avec interpolation HSL native
 | |
| - **Serveur** : Configuration mise en cache, transmission optimisée
 | |
| - **Base** : Index sur valeurs d'échelle
 | |
| - **Rendu** : Colorisation temps réel sans rechargement
 | |
| 
 | |
| ---
 | |
| 
 | |
| **Documentation maintenue à jour - Version 2025**  
 | |
| *Dernière modification : 9 août 2025 - Ajout dégradé HSL et indicateurs d'erreur* |