""" 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' [OK] {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' [OK] 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] 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' [OK] 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' [OK] Email envoyé à: {", ".join(recipients)}')) except Exception as e: self.stdout.write(self.style.ERROR(f' [ERREUR] 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)}'))