v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
199
visual_workflow_builder/backend/manage.py
Normal file
199
visual_workflow_builder/backend/manage.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de gestion des migrations de base de données VWB
|
||||
|
||||
Usage:
|
||||
python manage.py init # Initialiser les migrations (une seule fois)
|
||||
python manage.py migrate # Créer une nouvelle migration
|
||||
python manage.py upgrade # Appliquer les migrations pendantes
|
||||
python manage.py downgrade # Annuler la dernière migration
|
||||
python manage.py stamp # Marquer une révision comme appliquée
|
||||
python manage.py current # Afficher la révision actuelle
|
||||
python manage.py history # Afficher l'historique des migrations
|
||||
python manage.py backup # Sauvegarder la base de données
|
||||
python manage.py status # Afficher le status complet
|
||||
|
||||
Exemple de workflow de migration:
|
||||
1. Modifier les modèles dans db/models.py
|
||||
2. python manage.py migrate "Description du changement"
|
||||
3. Vérifier le fichier de migration généré dans migrations/versions/
|
||||
4. python manage.py upgrade
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
# Chemin vers le backend
|
||||
BACKEND_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
VENV_PYTHON = '/home/dom/ai/rpa_vision_v3/venv_v3/bin/python'
|
||||
VENV_FLASK = '/home/dom/ai/rpa_vision_v3/venv_v3/bin/flask'
|
||||
|
||||
# Variables d'environnement pour Flask
|
||||
ENV = os.environ.copy()
|
||||
ENV['FLASK_APP'] = 'app.py'
|
||||
|
||||
|
||||
def run_flask_db(args):
|
||||
"""Exécute une commande flask db"""
|
||||
cmd = [VENV_FLASK, 'db'] + args
|
||||
result = subprocess.run(cmd, cwd=BACKEND_DIR, env=ENV, capture_output=True, text=True)
|
||||
|
||||
# Filtrer les warnings connus
|
||||
output = result.stdout + result.stderr
|
||||
for line in output.split('\n'):
|
||||
if line.strip() and not any(skip in line for skip in [
|
||||
'FutureWarning', 'pynvml', 'use_fast', 'Workflow ignoré',
|
||||
'ScreenState non disponible', 'Server initialized'
|
||||
]):
|
||||
print(line)
|
||||
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
def find_database():
|
||||
"""Trouve le fichier de base de données SQLite"""
|
||||
instance_dir = os.path.join(BACKEND_DIR, 'instance')
|
||||
if os.path.exists(instance_dir):
|
||||
for f in os.listdir(instance_dir):
|
||||
if f.endswith('.db') and not f.startswith('.'):
|
||||
return os.path.join(instance_dir, f)
|
||||
return None
|
||||
|
||||
|
||||
def backup_database():
|
||||
"""Sauvegarde la base de données avant une migration"""
|
||||
instance_dir = os.path.join(BACKEND_DIR, 'instance')
|
||||
db_path = find_database()
|
||||
|
||||
if db_path and os.path.exists(db_path):
|
||||
db_name = os.path.basename(db_path).replace('.db', '')
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
backup_dir = os.path.join(instance_dir, 'backups')
|
||||
os.makedirs(backup_dir, exist_ok=True)
|
||||
backup_path = os.path.join(backup_dir, f'{db_name}_{timestamp}.db')
|
||||
shutil.copy2(db_path, backup_path)
|
||||
print(f"✅ Base de données sauvegardée: {backup_path}")
|
||||
|
||||
# Garder seulement les 10 dernières sauvegardes
|
||||
backups = sorted([f for f in os.listdir(backup_dir) if f.endswith('.db')])
|
||||
if len(backups) > 10:
|
||||
for old_backup in backups[:-10]:
|
||||
os.remove(os.path.join(backup_dir, old_backup))
|
||||
print(f" 🗑️ Ancienne sauvegarde supprimée: {old_backup}")
|
||||
|
||||
return backup_path
|
||||
else:
|
||||
print("⚠️ Pas de base de données existante à sauvegarder")
|
||||
return None
|
||||
|
||||
|
||||
def show_status():
|
||||
"""Affiche le status complet des migrations"""
|
||||
migrations_dir = os.path.join(BACKEND_DIR, 'migrations')
|
||||
instance_dir = os.path.join(BACKEND_DIR, 'instance')
|
||||
db_path = find_database()
|
||||
|
||||
print("=" * 50)
|
||||
print("Status des Migrations VWB")
|
||||
print("=" * 50)
|
||||
print(f"Dossier backend: {BACKEND_DIR}")
|
||||
print(f"Dossier migrations: {'✅ Existe' if os.path.exists(migrations_dir) else '❌ Non initialisé'}")
|
||||
print(f"Base de données: {'✅ ' + db_path if db_path else '❌ Non créée'}")
|
||||
|
||||
if os.path.exists(migrations_dir):
|
||||
versions_dir = os.path.join(migrations_dir, 'versions')
|
||||
if os.path.exists(versions_dir):
|
||||
migrations = sorted([f for f in os.listdir(versions_dir) if f.endswith('.py') and not f.startswith('__')])
|
||||
print(f"\nMigrations disponibles ({len(migrations)}):")
|
||||
for m in migrations:
|
||||
print(f" 📄 {m}")
|
||||
|
||||
# Backups
|
||||
backup_dir = os.path.join(instance_dir, 'backups')
|
||||
if os.path.exists(backup_dir):
|
||||
backups = sorted([f for f in os.listdir(backup_dir) if f.endswith('.db')])
|
||||
if backups:
|
||||
print(f"\nSauvegardes ({len(backups)}):")
|
||||
for b in backups[-5:]: # Afficher les 5 dernières
|
||||
print(f" 💾 {b}")
|
||||
|
||||
print("\nRévision actuelle de la base:")
|
||||
run_flask_db(['current'])
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
command = sys.argv[1].lower()
|
||||
|
||||
if command == 'init':
|
||||
print("🔧 Initialisation des migrations...")
|
||||
if run_flask_db(['init']):
|
||||
print("✅ Migrations initialisées")
|
||||
print(" Prochaine étape: python manage.py migrate 'Initial migration'")
|
||||
else:
|
||||
print("❌ Échec de l'initialisation")
|
||||
|
||||
elif command == 'migrate':
|
||||
message = ' '.join(sys.argv[2:]) if len(sys.argv) > 2 else f"Migration {datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
print(f"📝 Création de la migration: {message}")
|
||||
backup_database()
|
||||
if run_flask_db(['migrate', '-m', message]):
|
||||
print("✅ Migration créée")
|
||||
print(" Prochaine étape: python manage.py upgrade")
|
||||
else:
|
||||
print("❌ Échec de la création de migration")
|
||||
|
||||
elif command == 'upgrade':
|
||||
revision = sys.argv[2] if len(sys.argv) > 2 else 'head'
|
||||
print(f"⬆️ Application des migrations jusqu'à: {revision}")
|
||||
backup_database()
|
||||
if run_flask_db(['upgrade', revision]):
|
||||
print("✅ Migrations appliquées")
|
||||
else:
|
||||
print("❌ Échec de l'upgrade")
|
||||
|
||||
elif command == 'downgrade':
|
||||
revision = sys.argv[2] if len(sys.argv) > 2 else '-1'
|
||||
print(f"⬇️ Annulation des migrations: {revision}")
|
||||
backup_database()
|
||||
if run_flask_db(['downgrade', revision]):
|
||||
print("✅ Downgrade effectué")
|
||||
else:
|
||||
print("❌ Échec du downgrade")
|
||||
|
||||
elif command == 'stamp':
|
||||
revision = sys.argv[2] if len(sys.argv) > 2 else 'head'
|
||||
print(f"🏷️ Marquage de la révision: {revision}")
|
||||
if run_flask_db(['stamp', revision]):
|
||||
print("✅ Révision marquée")
|
||||
else:
|
||||
print("❌ Échec du stamp")
|
||||
|
||||
elif command == 'current':
|
||||
print("📍 Révision actuelle:")
|
||||
run_flask_db(['current'])
|
||||
|
||||
elif command == 'history':
|
||||
print("📜 Historique des migrations:")
|
||||
run_flask_db(['history'])
|
||||
|
||||
elif command == 'backup':
|
||||
backup_database()
|
||||
|
||||
elif command == 'status':
|
||||
show_status()
|
||||
|
||||
else:
|
||||
print(f"❌ Commande inconnue: {command}")
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user