Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| import sys | |
| import os | |
| import json | |
| import logging | |
| import asyncio | |
| import hashlib | |
| import requests | |
| from datetime import datetime | |
| # Dodanie 艣cie偶ki projektu do PYTHONPATH | |
| sys.path.append(os.path.join(os.path.dirname(__file__), "../..")) | |
| from backend.core.search.grant_search_service import grant_search_service | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") | |
| logger = logging.getLogger(__name__) | |
| CACHE_FILE = os.path.join(os.path.dirname(__file__), ".monitoring_cache.json") | |
| def load_cache(): | |
| if os.path.exists(CACHE_FILE): | |
| try: | |
| with open(CACHE_FILE, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| except Exception as e: | |
| logger.error(f"B艂膮d odczytu cache: {e}") | |
| return {} | |
| def save_cache(cache_data): | |
| try: | |
| with open(CACHE_FILE, "w", encoding="utf-8") as f: | |
| json.dump(cache_data, f, ensure_ascii=False, indent=4) | |
| except Exception as e: | |
| logger.error(f"B艂膮d zapisu cache: {e}") | |
| async def check_content_hash(url: str) -> str: | |
| """Pobiera zawarto艣膰 strony i zwraca hash SHA-256.""" | |
| try: | |
| response = await asyncio.to_thread(requests.get, url, timeout=10, allow_redirects=True) | |
| if response.status_code == 200: | |
| return hashlib.sha256(response.text.encode('utf-8')).hexdigest() | |
| except Exception as e: | |
| logger.warning(f"B艂膮d podczas pobierania tre艣ci {url}: {e}") | |
| return None | |
| async def monitor_grants(): | |
| logger.info("Rozpoczynam sprawdzanie zmian w regulaminach i terminach...") | |
| cache = load_cache() | |
| alerts = [] | |
| for source in grant_search_service.sources: | |
| if hasattr(source, "_get_verified_fallback"): | |
| fallback_list = source._get_verified_fallback() | |
| for grant in fallback_list: | |
| program_id = grant.get("id") or grant.get("name") | |
| url = grant.get("url", "") | |
| name = grant.get("name", "Brak nazwy") | |
| current_deadline = grant.get("deadline", "") | |
| if not program_id or not url.startswith("http"): | |
| continue | |
| logger.info(f"Monitorowanie: {name}") | |
| current_hash = await check_content_hash(url) | |
| if program_id in cache: | |
| prev_data = cache[program_id] | |
| prev_deadline = prev_data.get("deadline", "") | |
| prev_hash = prev_data.get("content_hash", "") | |
| changes = [] | |
| if current_deadline and current_deadline != prev_deadline: | |
| changes.append(f"Zmieniono termin z {prev_deadline} na {current_deadline}") | |
| if current_hash and prev_hash and current_hash != prev_hash: | |
| changes.append("Wykryto zmian臋 w tre艣ci strony (regulamin/og艂oszenie)") | |
| if changes: | |
| alerts.append(f"鈿狅笍 {name}:\n- " + "\n- ".join(changes) + f"\nLink: {url}") | |
| # Aktualizacja cache | |
| cache[program_id] = { | |
| "deadline": current_deadline, | |
| "content_hash": current_hash or cache.get(program_id, {}).get("content_hash", ""), | |
| "last_checked": datetime.now().isoformat() | |
| } | |
| save_cache(cache) | |
| # Wysy艂anie powiadomie艅 | |
| if alerts: | |
| logger.info(f"Wykryto {len(alerts)} zmian. Przygotowuj臋 powiadomienia administracyjne.") | |
| alert_body = "\n\n".join(alerts) | |
| try: | |
| from backend.gsd.email_notifier import send_hitl_email | |
| # U偶ywamy istniej膮cego powiadomiacza dla administrator贸w (DEFAULT_TARGET) | |
| # Wys艂anie jednej wiadomo艣ci ze wszystkimi alertami | |
| admin_email = os.environ.get("ADMIN_EMAIL", "bogmaz1@gmail.com") | |
| subject = "Dotacje AI: Zmiany w regulaminach lub terminach nabor贸w" | |
| # W send_hitl_email parametr to hitl_question, ale mo偶emy to lekko obej艣膰 buduj膮c tre艣膰 | |
| # Dla Fazy 1 wy艣lemy po prostu log / mail. | |
| logger.warning(f"[EMAIL DO {admin_email}]\nTemat: {subject}\n{alert_body}") | |
| send_hitl_email(f"ALERTY MONITORINGU:\n\n{alert_body}", "SYSTEM_MONITOR") | |
| logger.info("Wys艂ano e-mail do administrator贸w.") | |
| except Exception as e: | |
| logger.error(f"Nie uda艂o si臋 wys艂a膰 powiadomienia email: {e}") | |
| else: | |
| logger.info("Brak zmian w regulaminach i terminach.") | |
| if __name__ == "__main__": | |
| asyncio.run(monitor_grants()) | |