212 lines
7.9 KiB
Python
212 lines
7.9 KiB
Python
![]() |
#!/usr/bin/env python3
|
||
|
"""
|
||
|
Script de monitoring pour le scraper agenda du libre
|
||
|
Affiche les statistiques et l'état du scraper
|
||
|
"""
|
||
|
|
||
|
import json
|
||
|
import os
|
||
|
import sys
|
||
|
from datetime import datetime, timedelta
|
||
|
from typing import Dict, Any
|
||
|
|
||
|
class AgendaDuLibreMonitor:
|
||
|
def __init__(self, data_file: str = "agendadulibre_events.json"):
|
||
|
self.data_file = data_file
|
||
|
self.events_data = self.load_events_data()
|
||
|
|
||
|
def load_events_data(self) -> Dict[str, Any]:
|
||
|
"""Charge les données d'événements"""
|
||
|
if os.path.exists(self.data_file):
|
||
|
try:
|
||
|
with open(self.data_file, 'r', encoding='utf-8') as f:
|
||
|
return json.load(f)
|
||
|
except Exception as e:
|
||
|
print(f"❌ Erreur lors du chargement de {self.data_file}: {e}")
|
||
|
return {"events": {}, "last_update": None}
|
||
|
return {"events": {}, "last_update": None}
|
||
|
|
||
|
def get_statistics(self) -> Dict[str, Any]:
|
||
|
"""Calcule les statistiques des événements"""
|
||
|
events = self.events_data.get("events", {})
|
||
|
|
||
|
stats = {
|
||
|
"total_events": len(events),
|
||
|
"saved": 0,
|
||
|
"already_exists": 0,
|
||
|
"error": 0,
|
||
|
"unknown": 0,
|
||
|
"recent_errors": 0,
|
||
|
"last_update": self.events_data.get("last_update"),
|
||
|
"events_by_status": {},
|
||
|
"recent_events": []
|
||
|
}
|
||
|
|
||
|
# Analyser les statuts
|
||
|
for event_id, event_data in events.items():
|
||
|
status = event_data.get("status", "unknown")
|
||
|
stats["events_by_status"][status] = stats["events_by_status"].get(status, 0) + 1
|
||
|
|
||
|
if status == "saved":
|
||
|
stats["saved"] += 1
|
||
|
elif status == "already_exists":
|
||
|
stats["already_exists"] += 1
|
||
|
elif status == "error":
|
||
|
stats["error"] += 1
|
||
|
else:
|
||
|
stats["unknown"] += 1
|
||
|
|
||
|
# Vérifier les erreurs récentes (dernières 24h)
|
||
|
last_attempt = event_data.get("last_attempt")
|
||
|
if last_attempt and status == "error":
|
||
|
try:
|
||
|
attempt_time = datetime.fromisoformat(last_attempt.replace('Z', '+00:00'))
|
||
|
if datetime.now() - attempt_time.replace(tzinfo=None) < timedelta(hours=24):
|
||
|
stats["recent_errors"] += 1
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
# Collecter les événements récents (derniers 10)
|
||
|
if len(stats["recent_events"]) < 10:
|
||
|
event_info = {
|
||
|
"id": event_id,
|
||
|
"label": event_data.get("event", {}).get("properties", {}).get("label", "Sans titre"),
|
||
|
"status": status,
|
||
|
"last_attempt": last_attempt,
|
||
|
"message": event_data.get("message", "")
|
||
|
}
|
||
|
stats["recent_events"].append(event_info)
|
||
|
|
||
|
return stats
|
||
|
|
||
|
def display_statistics(self):
|
||
|
"""Affiche les statistiques de manière formatée"""
|
||
|
stats = self.get_statistics()
|
||
|
|
||
|
print("📊 Statistiques du scraper agenda du libre")
|
||
|
print("=" * 50)
|
||
|
|
||
|
# Informations générales
|
||
|
print(f"📁 Fichier de données: {self.data_file}")
|
||
|
print(f"📅 Dernière mise à jour: {stats['last_update'] or 'Jamais'}")
|
||
|
print(f"📈 Total d'événements traités: {stats['total_events']}")
|
||
|
print()
|
||
|
|
||
|
# Répartition par statut
|
||
|
print("📋 Répartition par statut:")
|
||
|
for status, count in stats["events_by_status"].items():
|
||
|
emoji = {
|
||
|
"saved": "✅",
|
||
|
"already_exists": "⚠️",
|
||
|
"error": "❌",
|
||
|
"unknown": "❓"
|
||
|
}.get(status, "❓")
|
||
|
print(f" {emoji} {status}: {count}")
|
||
|
print()
|
||
|
|
||
|
# Erreurs récentes
|
||
|
if stats["recent_errors"] > 0:
|
||
|
print(f"🚨 Erreurs récentes (24h): {stats['recent_errors']}")
|
||
|
print()
|
||
|
|
||
|
# Événements récents
|
||
|
if stats["recent_events"]:
|
||
|
print("🕒 Événements récents:")
|
||
|
for event in stats["recent_events"][:5]:
|
||
|
emoji = {
|
||
|
"saved": "✅",
|
||
|
"already_exists": "⚠️",
|
||
|
"error": "❌",
|
||
|
"unknown": "❓"
|
||
|
}.get(event["status"], "❓")
|
||
|
|
||
|
print(f" {emoji} {event['label'][:50]}{'...' if len(event['label']) > 50 else ''}")
|
||
|
if event["status"] == "error":
|
||
|
print(f" 💬 {event['message']}")
|
||
|
print()
|
||
|
|
||
|
# Recommandations
|
||
|
self.display_recommendations(stats)
|
||
|
|
||
|
def display_recommendations(self, stats: Dict[str, Any]):
|
||
|
"""Affiche des recommandations basées sur les statistiques"""
|
||
|
print("💡 Recommandations:")
|
||
|
|
||
|
if stats["total_events"] == 0:
|
||
|
print(" - Aucun événement traité. Exécutez le scraper pour commencer.")
|
||
|
elif stats["error"] > stats["saved"]:
|
||
|
print(" - Beaucoup d'erreurs détectées. Vérifiez la connectivité API.")
|
||
|
elif stats["recent_errors"] > 5:
|
||
|
print(" - Erreurs récentes nombreuses. Vérifiez les logs.")
|
||
|
elif stats["saved"] > 0:
|
||
|
print(" - Scraper fonctionne correctement.")
|
||
|
|
||
|
if stats["already_exists"] > stats["saved"]:
|
||
|
print(" - Beaucoup d'événements déjà existants. Le système de déduplication fonctionne.")
|
||
|
|
||
|
print()
|
||
|
|
||
|
def check_file_status(self):
|
||
|
"""Vérifie l'état du fichier de données"""
|
||
|
if not os.path.exists(self.data_file):
|
||
|
print(f"❌ Fichier de données non trouvé: {self.data_file}")
|
||
|
return False
|
||
|
|
||
|
try:
|
||
|
stat = os.stat(self.data_file)
|
||
|
size = stat.st_size
|
||
|
mtime = datetime.fromtimestamp(stat.st_mtime)
|
||
|
|
||
|
print(f"📁 État du fichier de données:")
|
||
|
print(f" - Taille: {size:,} octets")
|
||
|
print(f" - Dernière modification: {mtime}")
|
||
|
print(f" - Lisible: {'✅' if os.access(self.data_file, os.R_OK) else '❌'}")
|
||
|
print(f" - Écriture: {'✅' if os.access(self.data_file, os.W_OK) else '❌'}")
|
||
|
print()
|
||
|
|
||
|
return True
|
||
|
except Exception as e:
|
||
|
print(f"❌ Erreur lors de la vérification du fichier: {e}")
|
||
|
return False
|
||
|
|
||
|
def show_help(self):
|
||
|
"""Affiche l'aide"""
|
||
|
print("🔍 Monitor agenda du libre - Aide")
|
||
|
print("=" * 40)
|
||
|
print("Usage: python3 monitor_agendadulibre.py [options]")
|
||
|
print()
|
||
|
print("Options:")
|
||
|
print(" --stats, -s Afficher les statistiques (défaut)")
|
||
|
print(" --file, -f Vérifier l'état du fichier")
|
||
|
print(" --help, -h Afficher cette aide")
|
||
|
print()
|
||
|
print("Exemples:")
|
||
|
print(" python3 monitor_agendadulibre.py")
|
||
|
print(" python3 monitor_agendadulibre.py --file")
|
||
|
print(" python3 monitor_agendadulibre.py --stats")
|
||
|
|
||
|
def main():
|
||
|
"""Fonction principale"""
|
||
|
import argparse
|
||
|
|
||
|
parser = argparse.ArgumentParser(description="Monitor pour le scraper agenda du libre")
|
||
|
parser.add_argument("--stats", "-s", action="store_true", default=True,
|
||
|
help="Afficher les statistiques")
|
||
|
parser.add_argument("--file", "-f", action="store_true",
|
||
|
help="Vérifier l'état du fichier de données")
|
||
|
parser.add_argument("--data-file", default="agendadulibre_events.json",
|
||
|
help="Fichier de données à analyser")
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
monitor = AgendaDuLibreMonitor(args.data_file)
|
||
|
|
||
|
if args.file:
|
||
|
monitor.check_file_status()
|
||
|
|
||
|
if args.stats:
|
||
|
monitor.display_statistics()
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|