Spaces:
Running
Running
| """ | |
| Background scheduler dla GrantForge AI. | |
| Odświeża cache PARP i NCBR co 24h. | |
| Używa czystego asyncio — bez zewnętrznych zależności (APScheduler, Celery). | |
| Uruchamiany przez FastAPI lifespan context manager. | |
| """ | |
| import asyncio | |
| import logging | |
| from datetime import datetime, timezone | |
| logger = logging.getLogger(__name__) | |
| REFRESH_INTERVAL_HOURS = 6 | |
| _scheduler_task: asyncio.Task | None = None | |
| async def _refresh_grant_caches() -> None: | |
| """Odświeżenie cache wszystkich źródeł (Ultimate Grant Search Engine).""" | |
| from core.search.grant_search_service import grant_search_service | |
| started = datetime.now(timezone.utc).isoformat() | |
| logger.info(f"[Scheduler] Odświeżanie cache naborów dla wszystkich 9 źródeł (Faza 3) — {started}") | |
| try: | |
| results = await grant_search_service.get_all_grants(force_refresh=True) | |
| logger.info(f"[Scheduler] Pomyślnie zaktualizowano bazę naborów. Łączna liczba: {len(results)}") | |
| # Faza 6: Uruchomienie Compliance Guardian dla aktywnych projektów | |
| try: | |
| from agents.compliance_guardian import check_legal_updates | |
| # W środowisku produkcyjnym pobralibyśmy aktywne projekty z bazy | |
| # Tutaj testowo wysyłamy do admina | |
| check_legal_updates("global", "admin@grantforge.ai", "Wszystkie zaktualizowane programy") | |
| except Exception as e: | |
| logger.error(f"[Scheduler] Błąd modułu Compliance Guardian: {e}") | |
| except Exception as e: | |
| logger.error(f"[Scheduler] Błąd podczas globalnego odświeżania: {e}") | |
| async def _scheduler_loop() -> None: | |
| """Pętla działająca w tle: odśwież → czekaj 24h → powtórz.""" | |
| logger.info("[Scheduler] Uruchomiono background scheduler (interwał: 24h).") | |
| # Pierwsze uruchomienie po starcie serwera — małe opóźnienie żeby nie blokować startu | |
| await asyncio.sleep(10) | |
| while True: | |
| try: | |
| await _refresh_grant_caches() | |
| except Exception as e: | |
| logger.error(f"[Scheduler] Nieoczekiwany błąd: {e}") | |
| next_run = REFRESH_INTERVAL_HOURS * 3600 | |
| logger.info(f"[Scheduler] Następne odświeżanie za {REFRESH_INTERVAL_HOURS}h.") | |
| await asyncio.sleep(next_run) | |
| def start_scheduler() -> None: | |
| """Uruchamia scheduler jako asyncio task. Wywołaj z lifespan FastAPI.""" | |
| global _scheduler_task | |
| loop = asyncio.get_event_loop() | |
| _scheduler_task = loop.create_task(_scheduler_loop()) | |
| logger.info("[Scheduler] Task zarejestrowany.") | |
| def stop_scheduler() -> None: | |
| """Zatrzymuje scheduler. Wywołaj przy shutdown FastAPI.""" | |
| global _scheduler_task | |
| if _scheduler_task and not _scheduler_task.done(): | |
| _scheduler_task.cancel() | |
| logger.info("[Scheduler] Task anulowany.") | |
| _scheduler_task = None | |