`recommendation` n'avait aucun écrivain : les quatre couches de lecture étaient livrées, mais rien ne produisait de ligne. Le moteur comble ce trou. Le catalogue `REGLES` vit dans `app/services/`, pas dans `ml/` : il lit `alert.type`, `alert.severity`, `alert.value` et `alert.threshold`, sans modèle ni feature, et s'appuie sur deux repositories existants. L'arbitrage avec l'ADR 0005, qui annonçait #38 du côté ML, est tranché par l'ADR 0006. Sept règles, cinq par type d'alerte et deux transverses (sévérité critique, dépassement d'au moins 20 % du seuil), donc une à trois recommandations par alerte. L'idempotence est portée par la base : `create_missing()` insère en `ON CONFLICT DO NOTHING` sur `uq_recommendation_alert_rule`, ce qui supprime la fenêtre entre un contrôle préalable et l'insertion. `rule_reference` devient de ce fait une clé fonctionnelle, d'où le suffixe de version sur chaque référence. Deux déclencheurs : `POST /api/v1/recommendations/generate` réservé `admin`, et `python -m app.cli generate-recommendations` (cible `make recommendations`). Limite connue : aucune source n'alimente `alert` aujourd'hui, ni détection interne (#104) ni ingestion de l'API Mock. La route répond, le rapport reste à zéro, et la chaîne s'allume sans retoucher le moteur le jour où les alertes existent. Tests : 80 unitaires et API verts, plus 6 d'intégration dont l'idempotence jouée contre PostgreSQL. Closes #38
106 lines
4.1 KiB
Makefile
106 lines
4.1 KiB
Makefile
BACKEND := apps/backend
|
|
FRONTEND := apps/frontend
|
|
ML := ml
|
|
|
|
.DEFAULT_GOAL := help
|
|
.PHONY: help install install-backend install-frontend install-ml dev dev-backend dev-frontend \
|
|
lint format typecheck test test-cov test-integration check \
|
|
openapi docker-build db-up db-down db-reset db-logs db-psql migrate bootstrap-admin \
|
|
ml-lint ml-typecheck ml-test ml-check ml-train ml-score recommendations
|
|
|
|
help: ## Liste les cibles disponibles
|
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
|
|
|
|
install: install-backend install-frontend install-ml ## Installe les dépendances backend, frontend et ML
|
|
|
|
install-backend: ## Installe les dépendances du backend
|
|
cd $(BACKEND) && uv sync --all-groups
|
|
|
|
install-frontend: ## Installe les dépendances du frontend
|
|
cd $(FRONTEND) && npm ci
|
|
|
|
install-ml: ## Installe les dépendances du pipeline ML
|
|
cd $(ML) && uv sync --all-groups
|
|
|
|
dev: ## Lance toute la stack (backend + frontend) en rechargement à chaud
|
|
@trap 'kill 0' EXIT INT TERM; \
|
|
$(MAKE) --no-print-directory dev-backend & \
|
|
$(MAKE) --no-print-directory dev-frontend & \
|
|
wait
|
|
|
|
dev-backend: ## Lance l'API seule en rechargement à chaud
|
|
@echo "backend -> http://localhost:8000 (docs sur /docs)"
|
|
cd $(BACKEND) && uv run uvicorn app.main:create_app --factory --reload --host 0.0.0.0 --port 8000
|
|
|
|
dev-frontend: ## Lance le frontend seul en rechargement à chaud
|
|
@echo "frontend -> http://localhost:4200"
|
|
cd $(FRONTEND) && npm start
|
|
|
|
lint: ## Analyse statique du backend
|
|
cd $(BACKEND) && uv run ruff check .
|
|
|
|
format: ## Formate et corrige le backend
|
|
cd $(BACKEND) && uv run ruff format . && uv run ruff check --fix .
|
|
|
|
typecheck: ## Vérifie le typage du backend
|
|
cd $(BACKEND) && uv run mypy app
|
|
|
|
test: ## Exécute les tests backend ne demandant pas de base
|
|
cd $(BACKEND) && uv run pytest --cov-fail-under=85
|
|
|
|
test-cov: ## Rapports de couverture HTML et XML, plus les résultats au format JUnit
|
|
cd $(BACKEND) && uv run pytest --cov-fail-under=85 --cov-report=html \
|
|
--cov-report=xml --junitxml=test-results/junit.xml
|
|
|
|
test-integration: ## Exécute les tests exigeant une base joignable
|
|
cd $(BACKEND) && uv run pytest -m integration
|
|
|
|
check: lint typecheck test ## Chaîne de vérification complète
|
|
|
|
openapi: ## Régénère apps/backend/openapi.json depuis les routes déclarées
|
|
cd $(BACKEND) && uv run python -m app.cli export-openapi
|
|
|
|
ml-lint: ## Analyse statique du pipeline ML
|
|
cd $(ML) && uv run ruff check .
|
|
|
|
ml-typecheck: ## Vérifie le typage du pipeline ML
|
|
cd $(ML) && uv run mypy enervision_ml tests
|
|
|
|
ml-test: ## Exécute les tests du pipeline ML (donnees synthetiques, sans base ni serveur MLflow)
|
|
cd $(ML) && uv run pytest
|
|
|
|
ml-check: ml-lint ml-typecheck ml-test ## Chaîne de vérification complète du pipeline ML
|
|
|
|
ml-train: ## Entraine le modele LightGBM. CSV=chemin optionnel, sinon lit ML_DATABASE_URL
|
|
cd $(ML) && uv run python -m enervision_ml.train $(if $(CSV),--csv $(CSV),)
|
|
|
|
ml-score: ## Score le prochain pas horaire et l'ecrit dans `prediction`. CSV=chemin optionnel
|
|
cd $(ML) && uv run python -m enervision_ml.score $(if $(CSV),--csv $(CSV),)
|
|
|
|
recommendations: ## Genere les recommandations depuis les alertes en base. SITE=identifiant optionnel
|
|
cd $(BACKEND) && uv run python -m app.cli generate-recommendations $(if $(SITE),--site-id $(SITE),)
|
|
|
|
docker-build: ## Construit l'image du backend
|
|
docker build -t enervision-backend:local $(BACKEND)
|
|
|
|
db-up: ## Démarre la base PostgreSQL TimescaleDB
|
|
docker compose up -d db
|
|
|
|
db-down: ## Arrête la base en conservant ses données
|
|
docker compose stop db
|
|
|
|
db-reset: ## Détruit la base et rejoue db/init
|
|
docker compose down -v && docker compose up -d db
|
|
|
|
db-logs: ## Suit les journaux de la base
|
|
docker compose logs -f db
|
|
|
|
db-psql: ## Ouvre une session psql sur la base applicative
|
|
docker compose exec db psql -U $${POSTGRES_USER:-enervision} -d $${POSTGRES_DB:-enervision}
|
|
|
|
migrate: ## Applique les migrations Alembic
|
|
cd $(BACKEND) && uv run alembic upgrade head
|
|
|
|
bootstrap-admin: ## Crée le premier administrateur, mot de passe saisi au clavier
|
|
cd $(BACKEND) && uv run python -m app.cli create-admin --email $${EMAIL:?EMAIL=... requis}
|