feat: add management commands to initialize and run scheduled tasks

This commit is contained in:
Karl Determe 2026-08-14 20:56:38 +02:00
parent d3484e626d
commit 6fb2e00a54
2 changed files with 48 additions and 13 deletions

View file

@ -24,7 +24,7 @@ class Command(BaseCommand):
exports_dir = os.path.join('exports') exports_dir = os.path.join('exports')
if not os.path.exists(exports_dir): if not os.path.exists(exports_dir):
os.makedirs(exports_dir) os.makedirs(exports_dir)
self.stdout.write(self.style.SUCCESS(f'✓ Répertoire créé: {exports_dir}')) self.stdout.write(self.style.SUCCESS(f'[OK] Répertoire créé: {exports_dir}'))
# 1. Tâche d'export mensuel des interventions # 1. Tâche d'export mensuel des interventions
task_name = "Export mensuel des interventions" task_name = "Export mensuel des interventions"
@ -62,7 +62,7 @@ interventions_export_YYYY_MM.xlsx
task.next_run = task.calculate_next_run() task.next_run = task.calculate_next_run()
task.save() task.save()
self.stdout.write(self.style.SUCCESS(f'\n✓ Tâche créée: {task_name}')) self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name}'))
self.stdout.write(f' - Fréquence: {task.get_frequency_display()}') self.stdout.write(f' - Fréquence: {task.get_frequency_display()}')
self.stdout.write(f' - Planning: Le {task.day_of_month} de chaque mois à {task.hour:02d}:{task.minute:02d}') self.stdout.write(f' - Planning: Le {task.day_of_month} de chaque mois à {task.hour:02d}:{task.minute:02d}')
self.stdout.write(f' - Prochaine exécution: {task.next_run.strftime("%Y-%m-%d %H:%M")}') self.stdout.write(f' - Prochaine exécution: {task.next_run.strftime("%Y-%m-%d %H:%M")}')
@ -111,7 +111,7 @@ Données conservées dans le résumé:
task2.next_run = task2.calculate_next_run() task2.next_run = task2.calculate_next_run()
task2.save() task2.save()
self.stdout.write(self.style.SUCCESS(f'\n✓ Tâche créée: {task_name_analytics}')) self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name_analytics}'))
self.stdout.write(f' - Fréquence: {task2.get_frequency_display()}') self.stdout.write(f' - Fréquence: {task2.get_frequency_display()}')
self.stdout.write(f' - Planning: Tous les jours à {task2.hour:02d}:{task2.minute:02d}') self.stdout.write(f' - Planning: Tous les jours à {task2.hour:02d}:{task2.minute:02d}')
self.stdout.write(f' - Prochaine exécution: {task2.next_run.strftime("%Y-%m-%d %H:%M")}') self.stdout.write(f' - Prochaine exécution: {task2.next_run.strftime("%Y-%m-%d %H:%M")}')
@ -158,7 +158,7 @@ Arguments disponibles :
task3.next_run = task3.calculate_next_run() task3.next_run = task3.calculate_next_run()
task3.save() task3.save()
self.stdout.write(self.style.SUCCESS(f'\n✓ Tâche créée: {task_name_daily}')) self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name_daily}'))
self.stdout.write(f' - Fréquence: {task3.get_frequency_display()}') self.stdout.write(f' - Fréquence: {task3.get_frequency_display()}')
self.stdout.write(f' - Planning: Tous les jours à {task3.hour:02d}:{task3.minute:02d}') self.stdout.write(f' - Planning: Tous les jours à {task3.hour:02d}:{task3.minute:02d}')
self.stdout.write(f' - Statut: {"Active" if task3.is_active else "Inactive (à activer si nécessaire)"}') self.stdout.write(f' - Statut: {"Active" if task3.is_active else "Inactive (à activer si nécessaire)"}')
@ -200,7 +200,7 @@ Arguments disponibles :
task4.next_run = task4.calculate_next_run() task4.next_run = task4.calculate_next_run()
task4.save() task4.save()
self.stdout.write(self.style.SUCCESS(f'\n✓ Tâche créée: {task_name_reporting}')) self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name_reporting}'))
self.stdout.write(f' - Fréquence: {task4.get_frequency_display()}') self.stdout.write(f' - Fréquence: {task4.get_frequency_display()}')
self.stdout.write(f' - Planning: Tous les jours à {task4.hour:02d}:{task4.minute:02d}') self.stdout.write(f' - Planning: Tous les jours à {task4.hour:02d}:{task4.minute:02d}')
self.stdout.write(f' - Prochaine exécution: {task4.next_run.strftime("%Y-%m-%d %H:%M")}') self.stdout.write(f' - Prochaine exécution: {task4.next_run.strftime("%Y-%m-%d %H:%M")}')
@ -237,7 +237,7 @@ Les observations liées sont également clôturées.
task5.next_run = task5.calculate_next_run() task5.next_run = task5.calculate_next_run()
task5.save() task5.save()
self.stdout.write(self.style.SUCCESS(f'\n✓ Tâche créée: {task_name_auto_close}')) self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name_auto_close}'))
self.stdout.write(f' - Fréquence: {task5.get_frequency_display()}') self.stdout.write(f' - Fréquence: {task5.get_frequency_display()}')
self.stdout.write(f' - Planning: Tous les jours à {task5.hour:02d}:{task5.minute:02d}') self.stdout.write(f' - Planning: Tous les jours à {task5.hour:02d}:{task5.minute:02d}')
self.stdout.write(f' - Prochaine exécution: {task5.next_run.strftime("%Y-%m-%d %H:%M")}') self.stdout.write(f' - Prochaine exécution: {task5.next_run.strftime("%Y-%m-%d %H:%M")}')
@ -280,7 +280,7 @@ Elle effectue les actions suivantes :
task6.next_run = task6.calculate_next_run() task6.next_run = task6.calculate_next_run()
task6.save() task6.save()
self.stdout.write(self.style.SUCCESS(f'\n✓ Tâche créée: {task_name_backup}')) self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name_backup}'))
self.stdout.write(f' - Fréquence: {task6.get_frequency_display()}') self.stdout.write(f' - Fréquence: {task6.get_frequency_display()}')
self.stdout.write(f' - Planning: Tous les jours à {task6.hour:02d}:{task6.minute:02d}') self.stdout.write(f' - Planning: Tous les jours à {task6.hour:02d}:{task6.minute:02d}')
self.stdout.write(f' - Prochaine exécution: {task6.next_run.strftime("%Y-%m-%d %H:%M")}') self.stdout.write(f' - Prochaine exécution: {task6.next_run.strftime("%Y-%m-%d %H:%M")}')
@ -290,6 +290,41 @@ Elle effectue les actions suivantes :
if task6.next_run: if task6.next_run:
self.stdout.write(f' - Prochaine exécution: {task6.next_run.strftime("%Y-%m-%d %H:%M")}') self.stdout.write(f' - Prochaine exécution: {task6.next_run.strftime("%Y-%m-%d %H:%M")}')
# 7. Rappel par e-mail des notifications non lues
task_name_notif_reminders = "Rappels des notifications non lues"
task7, created7 = ScheduledCommand.objects.get_or_create(
name=task_name_notif_reminders,
defaults={
'command_name': 'send_unread_notification_reminders',
'command_args': {},
'frequency': 'hourly',
'minute': 0,
'is_active': True,
'send_email': False,
'description': '''
Envoie un e-mail de rappel aux utilisateurs ayant activé l'option et ayant des notifications non lues depuis plus que le délai configuré.
La tâche s'exécute toutes les heures.
Chaque utilisateur configure son délai et ses préférences dans la page /notifications/settings/.
'''.strip()
}
)
if created7:
task7.next_run = task7.calculate_next_run()
task7.save()
self.stdout.write(self.style.SUCCESS(f'\n[OK] Tâche créée: {task_name_notif_reminders}'))
self.stdout.write(f' - Fréquence: {task7.get_frequency_display()}')
self.stdout.write(f' - Planning: Toutes les heures à :00')
self.stdout.write(f' - Prochaine exécution: {task7.next_run.strftime("%Y-%m-%d %H:%M")}')
else:
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name_notif_reminders}'))
self.stdout.write(f' - Statut: {"Active" if task7.is_active else "Inactive"}')
if task7.next_run:
self.stdout.write(f' - Prochaine exécution: {task7.next_run.strftime("%Y-%m-%d %H:%M")}')
self.stdout.write(self.style.SUCCESS('\n=== Initialisation terminée ===')) self.stdout.write(self.style.SUCCESS('\n=== Initialisation terminée ==='))
self.stdout.write('\nPour gérer les tâches planifiées:') self.stdout.write('\nPour gérer les tâches planifiées:')
self.stdout.write(' 1. Accédez à l\'admin Django: /admin/') self.stdout.write(' 1. Accédez à l\'admin Django: /admin/')

View file

@ -39,7 +39,7 @@ class Command(BaseCommand):
for task in tasks: for task in tasks:
if task.should_run_now(): if task.should_run_now():
tasks_to_run.append(task) tasks_to_run.append(task)
self.stdout.write(self.style.SUCCESS(f' ✓ {task.name} doit être exécutée')) self.stdout.write(self.style.SUCCESS(f' [OK] {task.name} doit être exécutée'))
else: else:
next_run = task.next_run.strftime('%Y-%m-%d %H:%M') if task.next_run else 'Non planifiée' 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}') self.stdout.write(f' - {task.name} : prochaine exécution le {next_run}')
@ -101,7 +101,7 @@ class Command(BaseCommand):
task.next_run = task.calculate_next_run() task.next_run = task.calculate_next_run()
task.save() task.save()
self.stdout.write(self.style.SUCCESS(f' ✓ Succès (durée: {log.duration_seconds:.1f}s)')) self.stdout.write(self.style.SUCCESS(f' [OK] Succès (durée: {log.duration_seconds:.1f}s)'))
# Envoyer l'email si configuré # Envoyer l'email si configuré
if task.send_email and task.email_recipients: if task.send_email and task.email_recipients:
@ -125,7 +125,7 @@ class Command(BaseCommand):
task.next_run = task.calculate_next_run() task.next_run = task.calculate_next_run()
task.save() task.save()
self.stdout.write(self.style.ERROR(f' ✗ Erreur: {str(e)}')) self.stdout.write(self.style.ERROR(f' [ERREUR] Erreur: {str(e)}'))
# Envoyer l'email d'erreur si configuré # Envoyer l'email d'erreur si configuré
if task.send_email and task.email_recipients: if task.send_email and task.email_recipients:
@ -175,17 +175,17 @@ StreetUp - Système de tâches planifiées
# Attacher le fichier si il existe # Attacher le fichier si il existe
if output_file and os.path.exists(output_file): if output_file and os.path.exists(output_file):
email.attach_file(output_file) email.attach_file(output_file)
self.stdout.write(self.style.SUCCESS(f' ✓ Fichier attaché: {output_file}')) self.stdout.write(self.style.SUCCESS(f' [OK] Fichier attaché: {output_file}'))
email.send(fail_silently=False) email.send(fail_silently=False)
log.email_sent = True log.email_sent = True
log.save(update_fields=['email_sent']) log.save(update_fields=['email_sent'])
self.stdout.write(self.style.SUCCESS(f' ✓ Email envoyé à: {", ".join(recipients)}')) self.stdout.write(self.style.SUCCESS(f' [OK] Email envoyé à: {", ".join(recipients)}'))
except Exception as e: except Exception as e:
self.stdout.write(self.style.ERROR(f' ✗ Erreur lors de l\'envoi de l\'email: {str(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): def send_error_email(self, task, log):
""" """