loko/streetup/common/management/commands/run_scheduled_commands.py
2026-07-22 14:48:40 +02:00

242 lines
7.9 KiB
Python

"""
Commande Django pour exécuter les tâches planifiées.
Cette commande doit être appelée régulièrement (par exemple via cron toutes les heures).
Elle vérifie quelles tâches doivent être exécutées et les lance.
Usage:
python manage.py run_scheduled_commands
"""
from django.core.management.base import BaseCommand
from django.core.management import call_command
from django.utils import timezone
from django.core.mail import EmailMessage
from django.conf import settings
from common.models_scheduled_tasks import ScheduledCommand, CommandExecutionLog
from io import StringIO
import sys
import traceback
import os
class Command(BaseCommand):
help = 'Exécute les commandes planifiées qui doivent être exécutées'
def handle(self, *args, **options):
self.stdout.write(self.style.WARNING(f'\n=== Vérification des tâches planifiées - {timezone.now()} ===\n'))
# Récupérer toutes les tâches actives qui doivent être exécutées
tasks = ScheduledCommand.objects.filter(is_active=True)
if not tasks.exists():
self.stdout.write(self.style.WARNING('Aucune tâche planifiée active trouvée.'))
return
self.stdout.write(f'Nombre de tâches actives: {tasks.count()}')
tasks_to_run = []
for task in tasks:
if task.should_run_now():
tasks_to_run.append(task)
self.stdout.write(self.style.SUCCESS(f' ✓ {task.name} doit être exécutée'))
else:
next_run = task.next_run.strftime('%Y-%m-%d %H:%M') if task.next_run else 'Non planifiée'
self.stdout.write(f' - {task.name} : prochaine exécution le {next_run}')
if not tasks_to_run:
self.stdout.write(self.style.WARNING('\nAucune tâche à exécuter maintenant.'))
return
self.stdout.write(self.style.SUCCESS(f'\n{len(tasks_to_run)} tâche(s) à exécuter\n'))
# Exécuter chaque tâche
for task in tasks_to_run:
self.execute_task(task)
self.stdout.write(self.style.SUCCESS('\n=== Vérification terminée ===\n'))
def execute_task(self, task):
"""
Exécute une tâche planifiée.
"""
self.stdout.write(self.style.WARNING(f'\n>>> Exécution de: {task.name}'))
self.stdout.write(f' Commande: {task.command_name}')
self.stdout.write(f' Arguments: {task.command_args}')
# Créer un log d'exécution
log = CommandExecutionLog.objects.create(
scheduled_command=task,
status='running'
)
# Capturer la sortie
output = StringIO()
error_output = StringIO()
old_stdout = sys.stdout
old_stderr = sys.stderr
try:
# Rediriger stdout et stderr
sys.stdout = output
sys.stderr = error_output
# Exécuter la commande
call_command(task.command_name, **task.command_args)
# Restaurer stdout/stderr
sys.stdout = old_stdout
sys.stderr = old_stderr
# Marquer comme succès
log.status = 'success'
log.output = output.getvalue()
log.finished_at = timezone.now()
log.calculate_duration()
log.save()
# Mettre à jour la tâche
task.last_run = timezone.now()
task.next_run = task.calculate_next_run()
task.save()
self.stdout.write(self.style.SUCCESS(f' ✓ Succès (durée: {log.duration_seconds:.1f}s)'))
# Envoyer l'email si configuré
if task.send_email and task.email_recipients:
self.send_success_email(task, log)
except Exception as e:
# Restaurer stdout/stderr
sys.stdout = old_stdout
sys.stderr = old_stderr
# Marquer comme erreur
log.status = 'error'
log.output = output.getvalue()
log.error_message = f"{str(e)}\n\n{traceback.format_exc()}\n\n{error_output.getvalue()}"
log.finished_at = timezone.now()
log.calculate_duration()
log.save()
# Mettre à jour la tâche quand même
task.last_run = timezone.now()
task.next_run = task.calculate_next_run()
task.save()
self.stdout.write(self.style.ERROR(f' ✗ Erreur: {str(e)}'))
# Envoyer l'email d'erreur si configuré
if task.send_email and task.email_recipients:
self.send_error_email(task, log)
def send_success_email(self, task, log):
"""
Envoie un email de succès.
"""
try:
subject = task.email_subject or f"[StreetUp] Tâche planifiée réussie: {task.name}"
# Vérifier si un fichier a été généré
output_file = task.command_args.get('output', '')
body = f"""
Bonjour,
La tâche planifiée "{task.name}" s'est exécutée avec succès.
Détails:
- Commande: {task.command_name}
- Démarré: {log.started_at.strftime('%Y-%m-%d %H:%M:%S')}
- Terminé: {log.finished_at.strftime('%Y-%m-%d %H:%M:%S')}
- Durée: {log.duration_seconds:.1f} secondes
Prochaine exécution: {task.next_run.strftime('%Y-%m-%d %H:%M') if task.next_run else 'Non planifiée'}
---
Sortie de la commande:
{log.output}
---
Cordialement,
StreetUp - Système de tâches planifiées
"""
recipients = [email.strip() for email in task.email_recipients.split(',')]
email = EmailMessage(
subject=subject,
body=body,
from_email=settings.DEFAULT_FROM_EMAIL,
to=recipients,
)
# Attacher le fichier si il existe
if output_file and os.path.exists(output_file):
email.attach_file(output_file)
self.stdout.write(self.style.SUCCESS(f' ✓ Fichier attaché: {output_file}'))
email.send(fail_silently=False)
log.email_sent = True
log.save(update_fields=['email_sent'])
self.stdout.write(self.style.SUCCESS(f' ✓ Email envoyé à: {", ".join(recipients)}'))
except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Erreur lors de l\'envoi de l\'email: {str(e)}'))
def send_error_email(self, task, log):
"""
Envoie un email d'erreur.
"""
try:
subject = f"[StreetUp] ERREUR - Tâche planifiée: {task.name}"
body = f"""
Bonjour,
La tâche planifiée "{task.name}" a rencontré une erreur lors de son exécution.
Détails:
- Commande: {task.command_name}
- Démarré: {log.started_at.strftime('%Y-%m-%d %H:%M:%S')}
- Terminé: {log.finished_at.strftime('%Y-%m-%d %H:%M:%S')}
- Durée: {log.duration_seconds:.1f} secondes
Prochaine exécution: {task.next_run.strftime('%Y-%m-%d %H:%M') if task.next_run else 'Non planifiée'}
---
Message d'erreur:
{log.error_message}
---
Sortie de la commande:
{log.output}
---
Veuillez vérifier les logs dans l'administration Django pour plus de détails.
Cordialement,
StreetUp - Système de tâches planifiées
"""
recipients = [email.strip() for email in task.email_recipients.split(',')]
email = EmailMessage(
subject=subject,
body=body,
from_email=settings.DEFAULT_FROM_EMAIL,
to=recipients,
)
email.send(fail_silently=False)
log.email_sent = True
log.save(update_fields=['email_sent'])
self.stdout.write(self.style.WARNING(f' ! Email d\'erreur envoyé à: {", ".join(recipients)}'))
except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Erreur lors de l\'envoi de l\'email d\'erreur: {str(e)}'))