CoolFace
Apppublic

monish563/NU-KIOSK-API

sourceHugging Faceupdated 7mo agoView on Hugging Face
0likes
analysis_engine.py117 linesDownload Raw Back to tools
1"""Utility class that coordinates blueprint execution across data tables."""2 3from __future__ import annotations4 5from datetime import datetime6from typing import Any, Callable, Dict, Iterable, List, Optional7 8from ..data.catalog import DataCatalog9from .base import AnalysisContext, Blueprint, BlueprintResult, normalize_datetime10 11 12class AnalysisEngine:13    """14    Lightweight executor that keeps Satyrn-inspired components wired together.15 16    - Maintains a registry of blueprints.17    - Provides normalized event data to blueprints that need it.18    - Offers a fetch hook to refresh PlanIt Purple feeds.19    """20 21    def __init__(self, catalog: DataCatalog, blueprints: Optional[Iterable[Blueprint]] = None) -> None:22        self.catalog = catalog23        self._blueprints: Dict[str, Blueprint] = {}24        self._events: List[Dict[str, Any]] = []25        if blueprints:26            for blueprint in blueprints:27                self.register_blueprint(blueprint)28 29    def register_blueprint(self, blueprint: Blueprint) -> None:30        self._blueprints[blueprint.name] = blueprint31 32    def run(self, blueprint_name: str, **params: Any) -> BlueprintResult:33        if blueprint_name not in self._blueprints:34            raise KeyError(f"Blueprint '{blueprint_name}' not registered.")35        context = AnalysisContext(catalog=self.catalog, events=self._events)36        return self._blueprints[blueprint_name].run(context, **params)37 38    # ------------------------------------------------------------------ #39    # Event management40    # ------------------------------------------------------------------ #41    def refresh_events(self, fetch_fn: Optional[Callable[[List[str]], List[Dict[str, Any]]]] = None) -> int:42        """43        Update the in-memory event cache by downloading PlanIt Purple feeds.44 45        A custom fetch function can be provided for testing or offline use.46        Returns the number of events in the cache.47        """48        feed_config = self.catalog.metadata.get("event_feeds")49        urls = feed_config.get("urls") if isinstance(feed_config, dict) else None50        if not urls:51            self._events = []52            return 053 54        fetcher = fetch_fn or self._default_fetch55        events = fetcher(urls)56        events.sort(key=lambda evt: evt.get("start") or datetime.max)57        self._events = events58        return len(self._events)59 60    # Internal helpers -------------------------------------------------- #61    def _default_fetch(self, urls: List[str]) -> List[Dict[str, Any]]:62        try:63            import requests  # type: ignore64        except ImportError:65            return []66 67        events: List[Dict[str, Any]] = []68        for url in urls:69            try:70                response = requests.get(url, timeout=10)71                response.raise_for_status()72                payload = response.json()73            except Exception:74                continue75            events.extend(self._normalize_events(payload, source=url))76        return events77 78    def _normalize_events(self, payload: Any, *, source: str) -> List[Dict[str, Any]]:79        raw_events: List[Dict[str, Any]] = []80        if isinstance(payload, dict):81            if isinstance(payload.get("events"), list):82                raw_events = payload["events"]83            elif isinstance(payload.get("items"), list):84                raw_events = payload["items"]85            else:86                raw_events = [payload]87        elif isinstance(payload, list):88            raw_events = payload89        else:90            return []91 92        normalized: List[Dict[str, Any]] = []93        for item in raw_events:94            if not isinstance(item, dict):95                continue96            start_value = (97                item.get("start")98                or item.get("startDate")99                or item.get("start_date")100                or item.get("start_datetime")101            )102            end_value = item.get("end") or item.get("endDate") or item.get("end_date") or item.get("end_datetime")103            normalized.append(104                {105                    "title": item.get("title") or item.get("summary") or item.get("name") or "Untitled Event",106                    "start": normalize_datetime(start_value),107                    "end": normalize_datetime(end_value),108                    "location": item.get("location") or item.get("location_name"),109                    "description": item.get("description") or item.get("summary"),110                    "url": item.get("url") or item.get("permalink") or source,111                    "tags": item.get("tags") or item.get("keywords"),112                    "source": source,113                    "raw": item,114                }115            )116        return normalized117