# Notytex v2 - Système de Gestion Scolaire **Notytex** est une application web moderne pour la gestion complète des évaluations scolaires. Version 2.0 entièrement réécrite avec **FastAPI** (backend) et **Vue.js 3** (frontend). ## Objectif Principal Simplifier et digitaliser le processus d'évaluation scolaire, de la création des contrôles à la saisie des notes, en offrant une interface moderne, réactive et une API REST documentée. --- ## Architecture Technique | Couche | Technologie | |--------|-------------| | **Backend** | FastAPI 0.115+ (Python 3.11-3.13) | | **ORM** | SQLAlchemy 2.0.36+ avec aiosqlite (async) | | **Validation** | Pydantic 2.10+ / pydantic-settings | | **Serveur** | Uvicorn 0.32+ (ASGI) | | **Frontend** | Vue.js 3.5+ (Composition API) + Vite 6.0+ | | **State** | Pinia 2.2+ | | **CSS** | TailwindCSS 3.4+ | | **Graphiques** | Chart.js 4.4+ (vue-chartjs) | | **HTTP client** | Axios 1.7+ | | **Base de données** | SQLite (dev/prod), PostgreSQL possible | | **Déploiement** | Docker / Podman avec Nginx | | **Tests** | pytest + pytest-asyncio (99/99 tests) | | **Qualité** | Black, Ruff, ESLint | --- ## Modèle de Données (12 tables) ### Entités principales (7) ``` ClassGroup (6ème A, 5ème B...) ↓ StudentEnrollment (inscription temporelle : arrivée/départ) Student (élèves) ↓ Assessment (évaluation, rattachée à un trimestre 1/2/3) ↓ Exercise (exercices ordonnés) ↓ GradingElement (question/compétence, type "notes" ou "score", domaine optionnel) ↓ Grade (note individuelle par élève : valeur string + commentaire) ``` - **CouncilAppreciation** : appréciation de conseil de classe (élève + classe + trimestre, statut draft/finalized) ### Tables de configuration (5) - **AppConfig** : clé-valeur (ex: `context.school_year`, `grading.special_values`) - **CompetenceScaleValue** : échelle de notation (0=Non acquis → 3=Expert, couleurs, `.`, `d`, `a`) - **Competence** : compétences pédagogiques (Calculer, Raisonner...) avec couleur et icône - **Domain** : domaines/tags pour les éléments de notation Tous les modèles sont dans `backend/infrastructure/database/models.py`. --- ## Fonctionnalités Clés ### Gestion des Classes et Élèves - CRUD classes avec année scolaire - Inscription temporelle (bitemporal) : dates d'arrivée/départ, transferts, historique - Import CSV en masse (séparateur `;`, format "NOM Prénoms", gestion doublons) - Statistiques par trimestre et par classe ### Système d'Évaluation - Création unifiée (évaluation + exercices + barème en une fois) - Organisation par trimestre (1, 2, 3) - Filtrage avancé (trimestre, classe, statut de correction, tri) - Indicateurs de progression : rouge (0%), orange (en cours), vert (100%) ### Notation Dual Configurable **2 types de notation (par GradingElement) :** 1. **`notes`** : valeurs numériques décimales (ex: 15.5/20) — calcul direct 2. **`score`** : échelle fixe 0-3 — converti en points : `(value / 3) * max_points` **Valeurs spéciales (configurables via interface) :** - `.` = pas de réponse (compte comme 0 dans le total) - `d` = dispensé (exclu des calculs) - `a` = absent (compte comme 0) ### Analyse et Statistiques - Statistiques descriptives (moyenne, médiane, écart-type, min/max, quartiles) - Histogrammes de distribution (bins de 1 point, Chart.js) - Heatmaps par compétences et par domaines - Classement alphabétique avec scores par exercice ### Conseil de Classe - Préparation automatique avec données consolidées par trimestre - Saisie et historique des appréciations (brouillon → finalisé) - Vue complète des performances par élève ### Envoi de Bilans par Email - Génération automatique de bilans individualisés (HTML Jinja2) - SMTP configurable (Gmail, Outlook, serveur perso) - Envoi en lot avec rapport détaillé succès/erreurs - Prévisualisation avant envoi --- ## Structure du Code ``` notytex/ ├── backend/ # API FastAPI │ ├── api/ │ │ ├── main.py # App FastAPI + CORS + lifespan │ │ ├── dependencies.py # DI (AsyncSessionDep) │ │ └── routes/ │ │ ├── assessments.py # CRUD évaluations + notation + résultats + email │ │ ├── classes.py # CRUD classes + import CSV + stats dashboard │ │ ├── students.py # CRUD élèves + inscriptions │ │ ├── config.py # Configuration système + compétences + domaines │ │ └── council.py # Appréciations de conseil de classe │ │ │ ├── schemas/ # Modèles Pydantic (validation I/O) │ │ ├── common.py # BaseSchema, PaginationParams │ │ ├── assessment.py # AssessmentRead, AssessmentCreate... │ │ ├── student.py # StudentRead, StudentCreate... │ │ ├── grading.py # GradeRead, BulkGradeCreate │ │ ├── class_group.py # ClassGroupRead, ClassDashboardStats │ │ ├── config.py # ConfigRead, CompetenceCreate │ │ ├── council.py # CouncilAppreciationRead │ │ └── csv_import.py # CSVImportResponse │ │ │ ├── domain/ # Logique métier pure (aucune dépendance framework) │ │ ├── services/ │ │ │ ├── grading_calculator.py # Strategy Pattern (Notes/Score) │ │ │ ├── statistics_service.py # Statistiques descriptives │ │ │ ├── score_calculator.py # Calcul scores élèves │ │ │ ├── config_service.py # Valeurs spéciales, signification scores │ │ │ ├── student_report_service.py # Génération bilans email │ │ │ └── class_statistics_service.py # Stats par classe │ │ └── value_objects/ │ │ ├── progress.py # ProgressResult, ProgressStatus │ │ ├── score.py # GradeValue, ExerciseScore, StudentScore │ │ └── statistics.py # StatisticsResult, HistogramBin │ │ │ ├── infrastructure/ │ │ ├── database/ │ │ │ ├── models.py # 12 modèles SQLAlchemy │ │ │ └── session.py # AsyncSession factory │ │ └── external/ │ │ └── email_service.py # Service SMTP │ │ │ ├── core/ │ │ └── config.py # pydantic-settings (Settings) │ │ │ ├── tests/ # 99 tests │ │ ├── conftest.py # Fixtures (SQLite in-memory, AsyncClient) │ │ ├── unit/ # Tests services domaine │ │ ├── integration/ # Tests endpoints API │ │ └── comparison/ # Tests parité v1 ↔ v2 │ │ │ ├── pyproject.toml # Dépendances + config Black/Ruff/pytest │ └── uv.lock │ ├── frontend/ # SPA Vue.js 3 │ ├── src/ │ │ ├── main.js # Initialisation app │ │ ├── App.vue # Composant racine │ │ ├── router/index.js # 14 routes Vue Router │ │ ├── stores/ # Pinia (classes, assessments, config, notifications) │ │ ├── services/api.js # Instance Axios (/api/v2) │ │ ├── views/ # 14 pages (Dashboard, Grading, Results...) │ │ ├── components/ # 16+ composants réutilisables │ │ └── assets/ │ ├── vite.config.js # Proxy dev /api → localhost:8000 │ ├── tailwind.config.js │ └── package.json │ ├── docker/ │ ├── docker-compose.yaml # Production (ports 8080/8081) │ ├── docker-compose.dev.yaml # Développement avec hot reload │ └── .env.example # Template variables Docker │ ├── school_management.db # Base SQLite partagée ├── CLAUDE.md # Ce fichier └── README.md # Documentation utilisateur ``` --- ## Installation et Lancement ### Prérequis - Python 3.11-3.13 avec [uv](https://docs.astral.sh/uv/) - Node.js 22 LTS avec npm - Git ### Backend ```bash cd backend uv sync --all-extras uv run python -m uvicorn api.main:app --reload --port 8000 ``` - API : http://localhost:8000/api/v2/docs (Swagger) - ReDoc : http://localhost:8000/api/v2/redoc ### Frontend ```bash cd frontend npm install npm run dev ``` - Application : http://localhost:3000 (proxy automatique `/api` vers le backend) ### Docker / Podman ```bash cd docker cp .env.example .env # Modifier SECRET_KEY dans .env docker compose up -d # ou podman-compose up -d ``` - Frontend : http://localhost:8081 - API : http://localhost:8080/api/v2/docs --- ## Tests ```bash cd backend # Tous les tests uv run pytest tests/ -v # Tests unitaires uv run pytest tests/unit/ -v # Tests de parité v1 ↔ v2 uv run pytest tests/comparison/ -v # Avec couverture uv run pytest tests/ --cov=. --cov-report=html ``` **Résultat actuel : 99/99 tests** Les tests utilisent une base SQLite in-memory avec `AsyncClient` + `ASGITransport` pour tester les endpoints sans serveur. --- ## Configuration ### pydantic-settings (`backend/core/config.py`) ```python class Settings(BaseSettings): # Base de données database_url: Optional[str] # URL complète SQLAlchemy async database_path: Optional[str] # Chemin simple vers le .db # API api_v2_prefix: str = "/api/v2" # CORS cors_origins: list[str] = ["http://localhost:3000", "http://localhost:5173"] # Logging log_level: str = "INFO" # Email (optionnel) smtp_server, smtp_port, smtp_username, smtp_password, email_from model_config = SettingsConfigDict(env_file=".env") ``` Toute la config est lue depuis `backend/.env` et validée par Pydantic au démarrage. ### Configuration dynamique (table AppConfig) Paramètres modifiables à chaud via l'interface web : - `context.school_year` — année scolaire en cours - `grading.special_values` — valeurs spéciales (`.`, `d`, `a`) - `grading.score_meanings` — signification des scores 0-3 - Configuration SMTP pour l'envoi d'emails --- ## Patterns Architecturaux ### Strategy Pattern — Calcul de notation `domain/services/grading_calculator.py` : `GradingStrategy` (ABC) avec `NotesStrategy` et `ScoreStrategy`, sélectionnées par `GradingStrategyFactory` selon le `grading_type` du GradingElement. ### Value Objects — Objets immuables du domaine `domain/value_objects/` : `ProgressResult`, `GradeValue`, `StudentScore`, `StatisticsResult`, `HistogramBin`. Représentent des résultats de calcul sans identité propre. ### Dependency Injection — FastAPI natif ```python AsyncSessionDep = Annotated[AsyncSession, Depends(get_async_session)] ``` Injection de la session DB dans chaque route, facilitant le remplacement en tests. ### Service Layer — Logique métier découplée Les services dans `domain/services/` n'ont aucune dépendance sur FastAPI ou SQLAlchemy. Ils reçoivent des données brutes et retournent des Value Objects. ### Clean Architecture — Séparation en couches `api/` (routes) → `schemas/` (validation) → `domain/` (métier) → `infrastructure/` (DB, email). Les dépendances pointent toujours vers l'intérieur. --- ## Conventions de Code ### Backend (Python) - **Formatage** : `black` (line-length 88, target py311) - **Linting** : `ruff` (pycodestyle, pyflakes, isort, bugbear, comprehensions) - **Types** : `mypy` recommandé pour les nouvelles fonctions - **Async** : toutes les routes et accès DB sont `async/await` - **Noms** : snake_case, noms explicites (`calculate_student_scores`, pas `calc`) ### Frontend (JavaScript/Vue) - **Composition API** avec `