Audit et Plan de Refactorisation - app_optimized.py
Date: 2024-12-14
Fichier analysé: app/app_optimized.py
Lignes totales: 7098
Framework: FastAPI + SQLAlchemy async
PHASE 1: AUDIT ET VALIDATION
1.1 Statistiques du Fichier
| Métrique |
Valeur |
| Lignes de code |
7098 |
| Classes |
21 |
| Fonctions/Méthodes |
~150 |
| Endpoints API |
~65 |
| Modèles Pydantic |
28 |
| Services |
6 |
1.2 Structure Actuelle
app_optimized.py (7098 lignes)
├── Imports (L1-53)
├── Configuration globale (L55-100)
├── Utilitaires PDF/Markdown (L105-548)
├── Modèles Pydantic (L550-932)
├── Services métier (L937-2893)
│ ├── TaskLogService
│ ├── AdHocHistoryService
│ ├── BootstrapStatusService
│ ├── HostStatusService
│ └── SchedulerService
├── WebSocketManager (L2896-2929)
├── AnsibleService (L2933-3561)
├── Bootstrap SSH (L3564-3932)
├── HybridDB (L3935-4089)
├── Endpoints API (L4092-7023)
│ ├── Help/Documentation
│ ├── Hosts CRUD
│ ├── Groups management
│ ├── Tasks CRUD
│ ├── Logs CRUD
│ ├── Ansible execution
│ ├── Playbooks CRUD
│ ├── Bootstrap
│ ├── Ad-hoc history
│ ├── Health checks
│ ├── Schedules CRUD
│ └── Notifications
└── Startup/Shutdown events (L7025-7098)
1.3 Problèmes Identifiés
🔴 CRITIQUES (Bloquants)
| ID |
Problème |
Lignes |
Impact |
| C1 |
God Object Anti-pattern |
1-7098 |
Fichier monolithique de 7000+ lignes, impossible à maintenir |
| C2 |
Variables globales multiples |
2889-2893, 3561, 4089 |
État partagé entre modules (db, ansible_service, ws_manager, scheduler_service) |
| C3 |
Couplage fort |
Partout |
Services dépendent directement d'instances globales |
| C4 |
Imports circulaires potentiels |
44-53, 2105-2106 |
Mix from models.xxx et from app.models.xxx |
| C5 |
Duplication de modèles Pydantic |
550-932 |
Modèles dupliqués avec app/schemas/ existants |
🟠 AVERTISSEMENTS (À corriger)
| ID |
Problème |
Lignes |
Recommandation |
| W1 |
Fonctions trop longues |
3676-3932 (bootstrap_host: 256L) |
Extraire en sous-fonctions |
| W2 |
Import re dupliqué |
688, 706, 5406 |
Import unique en tête de fichier |
| W3 |
Syntaxe Pydantic dépréciée |
572-575, 589-592 |
class Config → model_config = ConfigDict(...) |
| W4 |
Print statements |
1995, 2008, 2058... |
Utiliser logging |
| W5 |
Gestion d'erreur inconsistante |
Multiple |
Standardiser avec exceptions personnalisées |
| W6 |
Magic strings |
564, 581, 597... |
Utiliser des Enums (Status, Level) |
| W7 |
Cache multi-niveau |
Multiple |
Risque de désynchronisation DB/mémoire |
| W8 |
Méthodes sync/async mélangées |
2141-2146 |
start() sync vs start_async() async |
| W9 |
Exceptions génériques |
2007, 2059... |
except Exception trop large |
| W10 |
Variables non utilisées |
Sporadique |
Nettoyer le code mort |
🔵 INFORMATIFS (Améliorations)
| ID |
Problème |
Recommandation |
| I1 |
Type hints manquants |
Ajouter types sur toutes les fonctions |
| I2 |
Docstrings mixtes FR/EN |
Uniformiser en français |
| I3 |
Constantes non regroupées |
Créer un module constants.py |
| I4 |
Tests unitaires insuffisants |
Ajouter tests pour chaque service |
| I5 |
Validation Pydantic incomplète |
Ajouter validators sur tous les champs critiques |
1.4 Analyse de la Complexité
Complexité Cyclomatique Élevée
| Fonction |
Lignes |
Complexité estimée |
Action |
bootstrap_host |
256 |
Très élevée |
Refactoriser en étapes |
_execute_schedule |
270 |
Très élevée |
Extraire logique métier |
execute_adhoc_command |
250 |
Élevée |
Simplifier gestion erreurs |
execute_ansible_playbook |
150 |
Modérée |
Extraire en service |
_markdown_to_pdf_bytes |
190 |
Élevée |
Module séparé |
Violations SOLID
| Principe |
Violation |
Exemple |
| Single Responsibility |
Oui |
Fichier fait tout (routing, services, models, utils) |
| Open/Closed |
Oui |
Ajout de fonctionnalité = modification du fichier |
| Liskov Substitution |
Non applicable |
- |
| Interface Segregation |
Oui |
Services trop couplés aux endpoints |
| Dependency Inversion |
Oui |
Dépendances hardcodées, pas d'injection |
PHASE 2: PLAN DE REFACTORISATION
2.1 Nouvelle Structure Proposée
projet/
├── main.py # Point d'entrée uvicorn
├── app/
│ ├── __init__.py # Factory create_app()
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config.py # Configuration centralisée
│ │ ├── constants.py # Constantes et enums
│ │ ├── exceptions.py # Exceptions personnalisées
│ │ └── dependencies.py # Injection de dépendances FastAPI
│ │
│ ├── models/ # (existant - conserver)
│ │ ├── __init__.py
│ │ ├── database.py
│ │ ├── host.py
│ │ ├── task.py
│ │ ├── log.py
│ │ ├── schedule.py
│ │ ├── schedule_run.py
│ │ └── user.py
│ │
│ ├── schemas/ # (existant - enrichir)
│ │ ├── __init__.py
│ │ ├── host.py # HostRequest, HostUpdateRequest, Host
│ │ ├── task.py # TaskRequest, Task, TaskLogFile
│ │ ├── group.py # GroupRequest, GroupUpdateRequest
│ │ ├── ansible.py # AnsibleExecutionRequest, AdHocCommandRequest
│ │ ├── schedule.py # Schedule*, ScheduleRun*, ScheduleRecurrence
│ │ ├── health.py # HealthCheck, SystemMetrics
│ │ └── common.py # CommandResult, LogEntry
│ │
│ ├── crud/ # (existant - conserver)
│ │ ├── __init__.py
│ │ ├── host.py
│ │ ├── task.py
│ │ ├── log.py
│ │ ├── schedule.py
│ │ └── schedule_run.py
│ │
│ ├── services/ # (existant - enrichir)
│ │ ├── __init__.py
│ │ ├── ansible_service.py # AnsibleService
│ │ ├── task_log_service.py # TaskLogService
│ │ ├── adhoc_history_service.py # AdHocHistoryService
│ │ ├── bootstrap_service.py # BootstrapStatusService + bootstrap_host
│ │ ├── host_status_service.py # HostStatusService
│ │ ├── scheduler_service.py # SchedulerService
│ │ ├── websocket_service.py # WebSocketManager
│ │ └── hybrid_db.py # HybridDB
│ │
│ ├── routes/
│ │ ├── __init__.py # Router aggregator
│ │ ├── hosts.py # /api/hosts/*
│ │ ├── groups.py # /api/groups/*
│ │ ├── tasks.py # /api/tasks/*
│ │ ├── logs.py # /api/logs/*
│ │ ├── ansible.py # /api/ansible/*
│ │ ├── playbooks.py # /api/playbooks/*
│ │ ├── schedules.py # /api/schedules/*
│ │ ├── adhoc.py # /api/adhoc/*
│ │ ├── bootstrap.py # /api/bootstrap/*
│ │ ├── health.py # /api/health/*
│ │ ├── notifications.py # /api/notifications/*
│ │ ├── help.py # /api/help/*
│ │ └── websocket.py # /ws
│ │
│ ├── utils/
│ │ ├── __init__.py
│ │ ├── pdf_generator.py # _markdown_to_pdf_bytes
│ │ ├── markdown_parser.py # _HelpHtmlToMarkdownParser
│ │ ├── ssh_utils.py # find_ssh_private_key, run_ssh_command
│ │ └── helpers.py # Fonctions utilitaires diverses
│ │
│ └── middleware/
│ ├── __init__.py
│ └── auth.py # verify_api_key (déjà dans auth_service)
│
├── tests/ # (existant - enrichir)
│ ├── __init__.py
│ ├── test_hosts.py
│ ├── test_tasks.py
│ ├── test_schedules.py
│ └── conftest.py
│
└── requirements.txt
2.2 Mapping Fonctionnalité → Fichier
| Fonctionnalité actuelle |
Lignes |
Nouveau fichier |
| Configuration |
55-100 |
app/core/config.py |
| Modèles Pydantic Host/Task |
560-800 |
app/schemas/host.py, app/schemas/task.py |
| Modèles Schedule |
803-932 |
app/schemas/schedule.py |
| Modèles Ad-hoc |
718-760 |
app/schemas/ansible.py |
| TaskLogService |
937-1568 |
app/services/task_log_service.py |
| AdHocHistoryService |
1571-1951 |
app/services/adhoc_history_service.py |
| BootstrapStatusService |
1954-2060 |
app/services/bootstrap_service.py |
| HostStatusService |
2063-2099 |
app/services/host_status_service.py |
| SchedulerService |
2102-2886 |
app/services/scheduler_service.py |
| WebSocketManager |
2896-2929 |
app/services/websocket_service.py |
| AnsibleService |
2933-3561 |
app/services/ansible_service.py |
| Bootstrap SSH functions |
3564-3932 |
app/services/bootstrap_service.py |
| HybridDB |
3935-4089 |
app/services/hybrid_db.py |
| Endpoints Hosts |
4224-4698 |
app/routes/hosts.py |
| Endpoints Groups |
4234-4416 |
app/routes/groups.py |
| Endpoints Tasks |
4691-5060 |
app/routes/tasks.py |
| Endpoints Logs |
5062-5134 |
app/routes/logs.py |
| Endpoints Ansible |
5157-5530 |
app/routes/ansible.py |
| Endpoints Playbooks |
5352-5491 |
app/routes/playbooks.py |
| Endpoints Ad-hoc |
5533-5782, 5972-6070 |
app/routes/adhoc.py |
| Endpoints Bootstrap |
5785-5969 |
app/routes/bootstrap.py |
| Endpoints Health |
5902-6112 |
app/routes/health.py |
| Endpoints Schedules |
6388-6949 |
app/routes/schedules.py |
| Endpoints Notifications |
6952-7022 |
app/routes/notifications.py |
| PDF/Markdown utils |
105-548 |
app/utils/pdf_generator.py, app/utils/markdown_parser.py |
| SSH utils |
3581-3673 |
app/utils/ssh_utils.py |
| Startup/Shutdown |
7025-7098 |
app/__init__.py (create_app) |
PHASE 3: ORDRE D'EXÉCUTION
Étape 1: Core (config, constants, exceptions, dependencies)
- Créer
app/core/__init__.py
- Créer
app/core/config.py - Centraliser toute la configuration
- Créer
app/core/constants.py - Enums et constantes
- Créer
app/core/exceptions.py - Exceptions personnalisées
- Créer
app/core/dependencies.py - DI FastAPI
Étape 2: Schemas (enrichir les existants)
- Enrichir
app/schemas/host.py
- Créer
app/schemas/task.py
- Créer
app/schemas/group.py
- Enrichir
app/schemas/ansible.py
- Créer
app/schemas/schedule.py
- Créer
app/schemas/health.py
- Créer
app/schemas/common.py
Étape 3: Services (nouveaux fichiers)
- Créer
app/services/ansible_service.py
- Créer
app/services/task_log_service.py
- Créer
app/services/adhoc_history_service.py
- Créer
app/services/bootstrap_service.py
- Créer
app/services/host_status_service.py
- Créer
app/services/scheduler_service.py
- Créer
app/services/websocket_service.py
- Créer
app/services/hybrid_db.py
Étape 4: Utils
- Créer
app/utils/__init__.py
- Créer
app/utils/pdf_generator.py
- Créer
app/utils/markdown_parser.py
- Créer
app/utils/ssh_utils.py
- Créer
app/utils/helpers.py
Étape 5: Routes (routers FastAPI)
- Créer
app/routes/__init__.py
- Créer tous les fichiers de routes
- Migrer chaque groupe d'endpoints
Étape 6: App Factory et Main
- Modifier
app/__init__.py - create_app()
- Créer
main.py - Point d'entrée
Étape 7: Nettoyage et Tests
- Supprimer code dupliqué de
app_optimized.py
- Mettre à jour les imports
- Adapter les tests existants
- Valider le fonctionnement
CONTRAINTES TECHNIQUES
À Maintenir
- ✅ 100% des fonctionnalités existantes
- ✅ Compatibilité avec les tests existants
- ✅ Support WebSocket temps réel
- ✅ Intégration SQLAlchemy async
- ✅ Intégration APScheduler
- ✅ Service de notifications ntfy
À Éviter
- ❌ Imports circulaires
- ❌ Variables globales (sauf configuration)
- ❌ Couplage fort entre modules
- ❌ Breaking changes dans l'API
Conventions
- PEP 8 stricte
- Type hints sur toutes les signatures
- Docstrings en français
- Logging au lieu de print()
- Tests unitaires pour nouveaux modules
ESTIMATION
| Phase |
Durée estimée |
Fichiers |
| Étape 1: Core |
~30 min |
5 fichiers |
| Étape 2: Schemas |
~45 min |
7 fichiers |
| Étape 3: Services |
~90 min |
8 fichiers |
| Étape 4: Utils |
~30 min |
5 fichiers |
| Étape 5: Routes |
~120 min |
13 fichiers |
| Étape 6: Factory |
~15 min |
2 fichiers |
| Étape 7: Tests |
~60 min |
Adaptation |
Total estimé: ~6-7 heures de travail
IMPLÉMENTATION TERMINÉE ✅
Structure Créée
projet/
├── main.py # ✅ Point d'entrée uvicorn
├── app/
│ ├── __init__.py # ✅ Export create_app()
│ ├── factory.py # ✅ Application factory
│ ├── core/
│ │ ├── __init__.py # ✅
│ │ ├── config.py # ✅ Configuration centralisée
│ │ ├── constants.py # ✅ Enums et constantes
│ │ ├── exceptions.py # ✅ Exceptions personnalisées
│ │ └── dependencies.py # ✅ Injection de dépendances
│ ├── schemas/
│ │ ├── common.py # ✅ CommandResult, LogEntry, etc.
│ │ ├── host_api.py # ✅ Host, HostRequest, etc.
│ │ ├── group.py # ✅ GroupRequest, etc.
│ │ ├── ansible.py # ✅ AnsibleExecutionRequest, etc.
│ │ ├── task_api.py # ✅ Task, TaskRequest, TaskLogFile
│ │ ├── schedule_api.py # ✅ Schedule, ScheduleRecurrence
│ │ └── health.py # ✅ HealthCheck
│ ├── services/
│ │ ├── websocket_service.py # ✅ WebSocketManager
│ │ ├── host_status_service.py # ✅ HostStatusService
│ │ ├── bootstrap_status_service.py # ✅ BootstrapStatusService
│ │ ├── task_log_service.py # ✅ TaskLogService
│ │ ├── adhoc_history_service.py # ✅ AdHocHistoryService
│ │ ├── ansible_service.py # ✅ AnsibleService
│ │ ├── scheduler_service.py # ✅ SchedulerService
│ │ └── hybrid_db.py # ✅ HybridDB
│ ├── routes/
│ │ ├── __init__.py # ✅ Router aggregator
│ │ ├── hosts.py # ✅ /api/hosts/*
│ │ ├── groups.py # ✅ /api/groups/*
│ │ ├── tasks.py # ✅ /api/tasks/*
│ │ ├── logs.py # ✅ /api/logs/*
│ │ ├── ansible.py # ✅ /api/ansible/*
│ │ ├── playbooks.py # ✅ /api/playbooks/*
│ │ ├── schedules.py # ✅ /api/schedules/*
│ │ ├── adhoc.py # ✅ /api/adhoc/*
│ │ ├── bootstrap.py # ✅ /api/bootstrap/*
│ │ ├── health.py # ✅ /api/health/*
│ │ ├── notifications.py # ✅ /api/notifications/*
│ │ ├── help.py # ✅ /api/help/*
│ │ └── websocket.py # ✅ /ws
│ └── utils/
│ ├── __init__.py # ✅
│ ├── ssh_utils.py # ✅ SSH & Bootstrap
│ ├── pdf_generator.py # ✅ Markdown to PDF
│ └── markdown_parser.py # ✅ HTML to Markdown
INSTRUCTIONS DE MIGRATION
1. Tester la nouvelle structure
# Depuis le répertoire racine du projet
cd c:\dev\git\python\homelab-automation-api-v2
# Démarrer avec le nouveau point d'entrée
python main.py
# Ou avec uvicorn
uvicorn main:app --host 0.0.0.0 --port 8008 --reload
2. Vérifier les imports
Si des erreurs d'import surviennent, vérifier que tous les modules sont correctement importés dans les fichiers __init__.py.
3. Migration progressive
L'ancienne application (app_optimized.py) reste fonctionnelle. La migration peut être progressive:
- Phase 1: Tester la nouvelle structure en parallèle
- Phase 2: Basculer le point d'entrée principal
- Phase 3: Supprimer
app_optimized.py une fois la validation complète
4. Points d'attention
| Élément |
Action requise |
| Tests existants |
Adapter les imports pour utiliser les nouveaux modules |
| Docker |
Modifier le CMD pour utiliser main.py au lieu de app_optimized.py |
| Scripts de démarrage |
Mettre à jour run_dev.sh et run_dev.ps1 |
| Variables d'environnement |
Aucun changement requis (même configuration) |
5. Rollback
En cas de problème, revenir à l'ancienne structure:
uvicorn app.app_optimized:app --host 0.0.0.0 --port 8008 --reload
AMÉLIORATIONS FUTURES
Court terme
Moyen terme
Long terme
Document généré automatiquement - Refactorisation complétée le 2024-12-14