334 lines
15 KiB
Python
334 lines
15 KiB
Python
"""
|
|
Script pour initialiser les tâches planifiées.
|
|
|
|
Ce script crée la tâche d'export mensuel des interventions.
|
|
|
|
Usage:
|
|
python manage.py init_scheduled_tasks
|
|
"""
|
|
|
|
from django.core.management.base import BaseCommand
|
|
from django.utils import timezone
|
|
from django.conf import settings
|
|
from common.models_scheduled_tasks import ScheduledCommand
|
|
import os
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = 'Initialise les tâches planifiées par défaut'
|
|
|
|
def handle(self, *args, **options):
|
|
self.stdout.write(self.style.WARNING('\n=== Initialisation des tâches planifiées ===\n'))
|
|
|
|
# Créer le répertoire exports s'il n'existe pas
|
|
exports_dir = os.path.join('exports')
|
|
if not os.path.exists(exports_dir):
|
|
os.makedirs(exports_dir)
|
|
self.stdout.write(self.style.SUCCESS(f'[OK] Répertoire créé: {exports_dir}'))
|
|
|
|
# 1. Tâche d'export mensuel des interventions
|
|
task_name = "Export mensuel des interventions"
|
|
|
|
task, created = ScheduledCommand.objects.get_or_create(
|
|
name=task_name,
|
|
defaults={
|
|
'command_name': 'export_interventions_xlsx',
|
|
'command_args': {
|
|
'output': 'exports/interventions_export_%(year)s_%(month)s.xlsx'
|
|
},
|
|
'frequency': 'monthly',
|
|
'hour': 4,
|
|
'minute': 0,
|
|
'day_of_month': 3,
|
|
'is_active': True,
|
|
'send_email': True,
|
|
'email_recipients': getattr(settings, 'EMAIL_INTERNAL_FOLLOWUP', 'kdeterme@sprb.brussels'),
|
|
'email_subject': '[StreetUp] Export mensuel des interventions',
|
|
'description': '''
|
|
Export mensuel automatique des interventions du mois précédent.
|
|
|
|
La tâche s'exécute le 3 de chaque mois à 4h du matin.
|
|
Les interventions exportées sont celles du mois précédent (exemple: si nous sommes en février, exporte janvier).
|
|
Un email est automatiquement envoyé avec le fichier Excel en pièce jointe.
|
|
|
|
Le fichier est sauvegardé dans le répertoire exports/ avec le nom:
|
|
interventions_export_YYYY_MM.xlsx
|
|
'''.strip()
|
|
}
|
|
)
|
|
|
|
if created:
|
|
# Calculer la prochaine exécution
|
|
task.next_run = task.calculate_next_run()
|
|
task.save()
|
|
|
|
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' - 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' - Email: {task.email_recipients}')
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name}'))
|
|
self.stdout.write(f' - Statut: {"Active" if task.is_active else "Inactive"}')
|
|
if task.next_run:
|
|
self.stdout.write(f' - Prochaine exécution: {task.next_run.strftime("%Y-%m-%d %H:%M")}')
|
|
|
|
# 2. Tâche de nettoyage quotidien des analytics
|
|
task_name_analytics = "Nettoyage mensuel des analytics"
|
|
|
|
task2, created2 = ScheduledCommand.objects.get_or_create(
|
|
name=task_name_analytics,
|
|
defaults={
|
|
'command_name': 'cleanup_analytics',
|
|
'command_args': {
|
|
'days': 30
|
|
},
|
|
'frequency': 'daily',
|
|
'hour': 3,
|
|
'minute': 0,
|
|
'is_active': True,
|
|
'send_email': False,
|
|
'description': '''
|
|
Nettoyage quotidien des données analytics de plus de 30 jours.
|
|
|
|
La tâche s'exécute tous les jours à 3h du matin.
|
|
Avant de supprimer les PageView et UserSession anciennes,
|
|
un résumé quotidien (DailyPageViewSummary) est créé pour
|
|
conserver les statistiques d'utilisation agrégées par jour.
|
|
|
|
Données conservées dans le résumé:
|
|
- Nombre total de pages vues
|
|
- Utilisateurs et sessions uniques
|
|
- Temps de réponse moyen et max
|
|
- Top 20 des pages visitées
|
|
- Top 20 des utilisateurs actifs
|
|
- Répartition par méthode HTTP
|
|
'''.strip()
|
|
}
|
|
)
|
|
|
|
if created2:
|
|
task2.next_run = task2.calculate_next_run()
|
|
task2.save()
|
|
|
|
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' - 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")}')
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name_analytics}'))
|
|
self.stdout.write(f' - Statut: {"Active" if task2.is_active else "Inactive"}')
|
|
if task2.next_run:
|
|
self.stdout.write(f' - Prochaine exécution: {task2.next_run.strftime("%Y-%m-%d %H:%M")}')
|
|
|
|
# 3. Tâche d'envoi groupé journalier des interventions
|
|
task_name_daily = "Résumé journalier des interventions"
|
|
|
|
task3, created3 = ScheduledCommand.objects.get_or_create(
|
|
name=task_name_daily,
|
|
defaults={
|
|
'command_name': 'send_daily_intervention_summary',
|
|
'command_args': {},
|
|
'frequency': 'daily',
|
|
'hour': 18,
|
|
'minute': 0,
|
|
'is_active': False, # Désactivé par défaut, à activer si des contrats utilisent l'envoi groupé
|
|
'send_email': False,
|
|
'description': '''
|
|
Envoie un résumé journalier des interventions pour les contrats configurés en mode "envoi groupé".
|
|
|
|
Au lieu d'envoyer un email par intervention, toutes les fiches PDF du jour sont
|
|
regroupées dans un seul email par contrat.
|
|
|
|
Pour activer l'envoi groupé sur un contrat :
|
|
1. Aller dans Admin > Contracts > Contrats > [contrat]
|
|
2. Cocher "Envoi groupé journalier"
|
|
3. Définir l'heure d'envoi souhaitée dans "Heure d'envoi du résumé journalier"
|
|
4. Activer cette tâche planifiée et configurer son heure en conséquence
|
|
|
|
Arguments disponibles :
|
|
--date YYYY-MM-DD : traiter une date spécifique (défaut: aujourd'hui)
|
|
--contract ID : traiter un seul contrat
|
|
--dry-run : simulation sans envoi réel
|
|
'''.strip()
|
|
}
|
|
)
|
|
|
|
if created3:
|
|
task3.next_run = task3.calculate_next_run()
|
|
task3.save()
|
|
|
|
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' - 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)"}')
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name_daily}'))
|
|
self.stdout.write(f' - Statut: {"Active" if task3.is_active else "Inactive"}')
|
|
if task3.next_run:
|
|
self.stdout.write(f' - Prochaine exécution: {task3.next_run.strftime("%Y-%m-%d %H:%M")}')
|
|
|
|
# 4. Rafraîchissement des vues matérialisées de reporting
|
|
task_name_reporting = "Rafraîchissement des vues matérialisées"
|
|
|
|
task4, created4 = ScheduledCommand.objects.get_or_create(
|
|
name=task_name_reporting,
|
|
defaults={
|
|
'command_name': 'refresh_reporting_views',
|
|
'command_args': {},
|
|
'frequency': 'daily',
|
|
'hour': 3,
|
|
'minute': 0,
|
|
'is_active': True,
|
|
'send_email': False,
|
|
'description': (
|
|
'Rafraîchit quotidiennement les vues matérialisées du schéma reporting.\n\n'
|
|
'Vues concernées (dans l\'ordre) :\n'
|
|
' - reporting.mv_location_hierarchy\n'
|
|
' - reporting.mv_interventions\n\n'
|
|
'Utilise REFRESH MATERIALIZED VIEW CONCURRENTLY pour ne pas bloquer les '
|
|
'lectures en cours d\'exécution.\n'
|
|
'Lors de la première exécution (ou avec --force-recreate), les vues sont '
|
|
'créées à partir des fichiers SQL dans reporting/sql/.\n\n'
|
|
'Arguments disponibles :\n'
|
|
' --force-recreate : force le DROP + CREATE des vues (bloquant).'
|
|
),
|
|
}
|
|
)
|
|
|
|
if created4:
|
|
task4.next_run = task4.calculate_next_run()
|
|
task4.save()
|
|
|
|
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' - 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")}')
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name_reporting}'))
|
|
self.stdout.write(f' - Statut: {"Active" if task4.is_active else "Inactive"}')
|
|
if task4.next_run:
|
|
self.stdout.write(f' - Prochaine exécution: {task4.next_run.strftime("%Y-%m-%d %H:%M")}')
|
|
|
|
# 5. Clôture automatique des interventions
|
|
task_name_auto_close = "Clôture automatique des interventions"
|
|
|
|
task5, created5 = ScheduledCommand.objects.get_or_create(
|
|
name=task_name_auto_close,
|
|
defaults={
|
|
'command_name': 'close_expired_interventions',
|
|
'command_args': {},
|
|
'frequency': 'daily',
|
|
'hour': 2,
|
|
'minute': 0,
|
|
'is_active': True,
|
|
'send_email': False,
|
|
'description': '''
|
|
Clôture automatique des interventions après un certain délai défini par contrat.
|
|
|
|
La tâche s'exécute tous les jours à 2h00 du matin.
|
|
Les interventions concernées sont celles dont le statut est finalisé, traité, validé ou facturé, et dont le délai depuis la fin est supérieur au délai défini sur leur contrat.
|
|
Les observations liées sont également clôturées.
|
|
'''.strip()
|
|
}
|
|
)
|
|
|
|
if created5:
|
|
task5.next_run = task5.calculate_next_run()
|
|
task5.save()
|
|
|
|
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' - 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")}')
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name_auto_close}'))
|
|
self.stdout.write(f' - Statut: {"Active" if task5.is_active else "Inactive"}')
|
|
if task5.next_run:
|
|
self.stdout.write(f' - Prochaine exécution: {task5.next_run.strftime("%Y-%m-%d %H:%M")}')
|
|
|
|
# 6. Sauvegarde de la base de données
|
|
task_name_backup = "Sauvegarde de la base de données"
|
|
|
|
task6, created6 = ScheduledCommand.objects.get_or_create(
|
|
name=task_name_backup,
|
|
defaults={
|
|
'command_name': 'backup_database',
|
|
'command_args': {},
|
|
'frequency': 'daily',
|
|
'hour': 4,
|
|
'minute': 0,
|
|
'is_active': True,
|
|
'send_email': True,
|
|
'email_recipients': getattr(settings, 'EMAIL_INTERNAL_FOLLOWUP', 'kdeterme@sprb.brussels'),
|
|
'email_subject': '[StreetUp] Sauvegarde de la base de données',
|
|
'description': '''
|
|
Sauvegarde quotidienne de la base de données PostgreSQL.
|
|
|
|
La tâche s'exécute tous les jours à 4h00 du matin.
|
|
Elle effectue les actions suivantes :
|
|
1. Crée un dump PostgreSQL (pg_dump) des schémas django et public.
|
|
2. Compresse le dump en fichier .sql.gz.
|
|
3. Téléverse le backup compressé vers le stockage distant MinIO (si BACKUP_MINIO_ENABLED est à true).
|
|
4. Synchronise le backup vers le serveur de test via Rsync (si BACKUP_REMOTE_ENABLED est à true).
|
|
5. Supprime les sauvegardes locales de plus de 30 jours (configuré via BACKUP_RETENTION_DAYS).
|
|
'''.strip()
|
|
}
|
|
)
|
|
|
|
if created6:
|
|
task6.next_run = task6.calculate_next_run()
|
|
task6.save()
|
|
|
|
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' - 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")}')
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f'\n! Tâche déjà existante: {task_name_backup}'))
|
|
self.stdout.write(f' - Statut: {"Active" if task6.is_active else "Inactive"}')
|
|
if task6.next_run:
|
|
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('\nPour gérer les tâches planifiées:')
|
|
self.stdout.write(' 1. Accédez à l\'admin Django: /admin/')
|
|
self.stdout.write(' 2. Section: Common > Commandes planifiées')
|
|
self.stdout.write('\nPour tester l\'exécution:')
|
|
self.stdout.write(' python manage.py run_scheduled_commands')
|
|
self.stdout.write('\n')
|